A autenticação é a pedra angular de qualquer aplicação web moderna, mas configurá-la continua sendo um dos desafios mais demorados que os desenvolvedores enfrentam. Apresentamos a Better Auth API — uma solução de autenticação agnóstica a frameworks que promete transformar a forma como implementamos o gerenciamento de usuários. Neste guia completo, vamos construir uma aplicação full-stack completa que demonstra o poder e a simplicidade da Better Auth, usando Bun para um desempenho incrivelmente rápido.
Quer uma plataforma integrada e completa para sua Equipe de Desenvolvedores trabalhar com máxima produtividade?
Apidog atende a todas as suas demandas e substitui o Postman por um preço muito mais acessível!
O que é a Better Auth API?
A Better Auth API é um framework de autenticação moderno e de código aberto, projetado para funcionar perfeitamente em qualquer ambiente JavaScript. Ao contrário das bibliotecas de autenticação tradicionais que o prendem a ecossistemas específicos, a Better Auth oferece uma API unificada que se adapta à sua stack — seja você usando React, Vue, Svelte ou JavaScript puro no frontend, e Node.js, Bun ou Deno no backend. Sua arquitetura baseada em plugins suporta múltiplas estratégias de autenticação, desde e-mail/senha tradicionais até provedores OAuth, passkeys e links mágicos, tudo isso mantendo a segurança de tipos e uma excelente experiência para o desenvolvedor.

Começando com Better Auth: Pré-requisitos e Configuração do Projeto
Antes de mergulhar no código, certifique-se de ter o seguinte instalado:
- Bun 1.0+: Usaremos Bun ao longo deste tutorial por sua velocidade excepcional e ferramentas modernas.
- Node.js 18+: Necessário para o runtime do backend.
- Um editor de código: VS Code recomendado para suporte a TypeScript.

Embora esta configuração funcione perfeitamente com npm, demonstraremos o fluxo de trabalho do Bun, que oferece instalação de pacotes 3-5 vezes mais rápida e uma experiência de desenvolvimento mais simplificada.
Construindo um Projeto de Exemplo: Implementação Passo a Passo
Vamos criar um sistema de autenticação prático com um frontend React e um backend Express, completo com persistência de banco de dados.
Passo 1: Configuração do Backend com Express e Drizzle ORM
1. Inicializar o Projeto Backend
Primeiro, crie e entre no seu diretório backend:
mkdir better-auth-backend
cd better-auth-backend
bun init -y
2. Instalar Dependências
Precisaremos do Express para o servidor, Better Auth para autenticação e Drizzle ORM para gerenciamento de banco de dados:
bun add express better-auth drizzle-orm
bun add -D @types/bun @types/express drizzle-kit
3. Configurar Variáveis de Ambiente
Crie um arquivo .env para armazenar configurações sensíveis:
BETTER_AUTH_SECRET=your-secret-key-here # Generate with: openssl rand -base64 32
BETTER_AUTH_URL=http://localhost:3000
DATABASE_URL=local.db
O BETTER_AUTH_SECRET também pode ser gerado no site da better-auth.
4. Criar Esquema de Banco de Dados com Drizzle ORM
Better Auth funciona melhor com Drizzle, que oferece excelente suporte a TypeScript e evita problemas com módulos nativos. Crie src/db/schema.ts:
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
export const user = sqliteTable("user", {
id: text("id").primaryKey(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
emailVerified: integer('email_verified', { mode: 'boolean' }).notNull().default(false),
image: text('image'),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull(),
});
export const session = sqliteTable("session", {
id: text("id").primaryKey(),
expiresAt: integer('expires_at', { mode: 'timestamp' }).notNull(),
token: text('token').notNull().unique(),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
userId: text('user_id').notNull(),
});
export const account = sqliteTable("account", {
id: text("id").primaryKey(),
accountId: text('account_id').notNull(),
providerId: text('provider_id').notNull(),
userId: text('user_id').notNull(),
accessToken: text('access_token'),
refreshToken: text('refresh_token'),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
});
export const verification = sqliteTable("verification", {
id: text("id").primaryKey(),
identifier: text('identifier').notNull(),
value: text('value').notNull(),
expiresAt: integer('expires_at', { mode: 'timestamp' }).notNull(),
});
5. Configurar Conexão com o Banco de Dados
Crie src/db/index.ts usando a ligação nativa do Bun para SQLite:
import { Database } from "bun:sqlite";
import { drizzle } from "drizzle-orm/bun-sqlite";
const sqlite = new Database("local.db");
export const db = drizzle(sqlite);
6. Configurar Better Auth
Crie src/lib/auth.ts para configurar a Better Auth API:
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "../db";
import { user, session, account, verification } from "../db/schema";
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: "sqlite",
schema: {
user: user,
session: session,
account: account,
verification: verification,
},
}),
emailAndPassword: {
enabled: true,
},
trustedOrigins: ["http://localhost:5173"],
});
7. Criar Servidor Express
Em src/index.ts, monte o handler da Better Auth API:
import express from "express";
import cors from "cors";
import { toNodeHandler } from "better-auth/node";
import { auth } from "./lib/auth";
const app = express();
const PORT = process.env.PORT || 3000;
app.use(
cors({
origin: "http://localhost:5173",
credentials: true,
})
);
// Mount Better Auth API at /api/auth
app.use("/api/auth", toNodeHandler(auth));
app.use(express.json());
app.get("/api/me", async (req, res) => {
const session = await auth.api.getSession({
headers: req.headers,
});
if (!session) {
return res.status(401).json({ error: "Unauthorized" });
}
res.json({ user: session.user });
});
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
8. Executar Migração do Banco de Dados
Crie drizzle.config.ts na raiz do backend:
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "sqlite",
schema: "./src/db/schema.ts",
out: "./drizzle",
dbCredentials: {
url: "local.db",
},
});
Execute a migração para criar as tabelas:
bunx drizzle-kit push

Passo 2: Configuração do Frontend com React e Vite
1. Criar Aplicação React
Em um novo terminal, inicialize o frontend:
bun create vite better-auth-frontend --template react-ts
cd better-auth-frontend
2. Instalar Dependências
bun add better-auth
3. Configurar Tailwind CSS (Atualização V4)
O Tailwind CSS v4 exige uma configuração diferente. Instale os novos pacotes:
bun add -D tailwindcss postcss @tailwindcss/postcss
Crie tailwind.config.js na raiz do projeto:
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
Crie postcss.config.js:
export default {
plugins: {
"@tailwindcss/postcss": {},
},
}
Crie src/index.css:
@import "tailwindcss";
body {
margin: 0;
font-family: system-ui, -apple-system, sans-serif;
}
4. Configurar Cliente Better Auth
Crie src/lib/auth-client.ts:
import { createAuthClient } from "better-auth/react";
export const authClient = createAuthClient({
baseURL: "http://localhost:3000",
});
export const { signIn, signUp, useSession } = authClient;
5. Construir a UI de Autenticação
Substitua src/App.tsx por uma interface de autenticação completa:
import { useState } from 'react';
import { useSession, signIn, signUp } from './lib/auth-client';
function App() {
const { data: session, isPending } = useSession();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [name, setName] = useState('');
const handleSignUp = async (e: React.FormEvent) => {
e.preventDefault();
await signUp.email({
name,
email,
password,
callbackURL: '/',
});
};
const handleSignIn = async (e: React.FormEvent) => {
e.preventDefault();
await signIn.email({
email,
password,
});
};
const handleSignOut = async () => {
await authClient.signOut();
};
if (isPending) return <div className="flex items-center justify-center min-h-screen">Carregando...</div>;
return (
<div className="min-h-screen bg-gray-100 flex items-center justify-center p-4">
<div className="max-w-md w-full bg-white rounded-lg shadow-md p-6">
<h1 className="text-2xl font-bold text-center mb-6 text-gray-800">
Teste da Better Auth API
</h1>
{session?.user ? (
<div className="space-y-4">
<div className="bg-green-50 p-4 rounded-md">
<p className="text-green-800 font-semibold">Logado como:</p>
<p className="text-green-700">{session.user.email}</p>
<p className="text-green-600 text-sm">{session.user.name}</p>
</div>
<button
onClick={handleSignOut}
className="w-full bg-red-500 hover:bg-red-600 text-white font-medium py-2 px-4 rounded-md transition"
>
Sair
</button>
</div>
) : (
<div className="space-y-4">
<form onSubmit={handleSignUp} className="space-y-3">
<h2 className="text-lg font-semibold text-gray-700">Cadastrar</h2>
<input
type="text"
placeholder="Nome"
value={name}
onChange={(e) => setName(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
<input
type="password"
placeholder="Senha"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
<button
type="submit"
className="w-full bg-green-500 hover:bg-green-600 text-white font-medium py-2 px-4 rounded-md transition"
>
Criar Conta
</button>
</form>
<form onSubmit={handleSignIn} className="space-y-3">
<h2 className="text-lg font-semibold text-gray-700">Entrar</h2>
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
<input
type="password"
placeholder="Senha"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
<button
type="submit"
className="w-full bg-blue-500 hover:bg-blue-600 text-white font-medium py-2 px-4 rounded-md transition"
>
Entrar
</button>
</form>
</div>
)}
</div>
</div>
);
}
export default App;

Passo 3: Testando a Integração
1. Iniciar Servidor Backend
cd better-auth-backend
bun dev

2. Iniciar Desenvolvimento Frontend
cd better-auth-frontend
bun dev

3. Testar Fluxo de Autenticação
- Abra
http://localhost:5173no seu navegador - Registre um novo usuário usando o formulário de Cadastro


- Observe o arquivo do banco de dados crescendo à medida que as tabelas são preenchidas
- Faça logout e login novamente para verificar o gerenciamento de sessão
- Verifique o endpoint da sessão:
http://localhost:3000/api/auth/session

Principais Benefícios da Better Auth API
A Better Auth API se destaca por várias vantagens convincentes:
- Agnosticismo de Framework: Ao contrário do NextAuth.js ou Firebase Auth, a Better Auth funciona em qualquer lugar onde o JavaScript é executado. A mesma lógica de autenticação atende a clientes web, mobile e de API sem modificação.
- Múltiplas Estratégias de Autenticação: Suporte pronto para uso para credenciais, OAuth 2.0, passkeys, autenticação de dois fatores e links mágicos. Cada estratégia é um plugin que pode ser ativado com uma única linha de configuração.
- Segurança de Tipos: Suporte completo a TypeScript com tipos inferidos do seu esquema de banco de dados, eliminando o "inferno do tipo any" comum em bases de código de autenticação.
- Flexibilidade de Banco de Dados: O adaptador Drizzle ORM significa que você pode alternar entre SQLite, PostgreSQL e MySQL sem alterar sua lógica de autenticação. Este tutorial usa SQLite para simplificar, mas o mesmo código escala para bancos de dados de produção.
- Ecossistema de Plugins: Precisa de suporte a organização? Multi-tenancy? Funções de administrador? O sistema de plugins da Better Auth permite estender a funcionalidade sem inchar o core.
- Desempenho: Com Bun como runtime, as inicializações a frio são inferiores a 100ms, e todo o fluxo de autenticação é concluído em menos de 50ms em hardware modesto.
Perguntas Frequentes
P1: Posso usar a Better Auth API com npm em vez de Bun?
Resp: Com certeza. Embora este guia utilize o Bun por seus benefícios de desempenho, cada comando tem um equivalente npm. Substitua bun add por npm install, bun dev por npm run dev e bunx por npx. O único código específico do Bun é a importação bun:sqlite, que pode ser substituída por better-sqlite3 para ambientes Node.js.
P2: Por que precisamos do Drizzle ORM? A Better Auth não pode criar tabelas automaticamente?
Resp: A Better Auth segue o princípio do gerenciamento explícito de banco de dados. O Drizzle fornece migrações com segurança de tipos, versionamento de esquema e previne a perda acidental de dados. O comando drizzle-kit push é uma configuração única que lhe dá controle total sobre a evolução do seu banco de dados.
P3: E se eu encontrar o erro "Missing parameter name"?
Resp: Isso ocorre ao usar app.all() com wildcards no Express. A solução é usar app.use("/api/auth", toNodeHandler(auth)) em vez disso. O handler da Better Auth gerencia todas as sub-rotas internamente, então o Express não precisa de correspondência com wildcards.
P4: Como adiciono provedores de autenticação social?
Resp: Habilite os plugins OAuth na sua configuração da Better Auth. Por exemplo, para adicionar o GitHub:
import { betterAuth } from "better-auth";
import { github } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
github({
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
})
]
});
P5: A Better Auth API está pronta para produção?
Resp: Sim. A Better Auth impulsiona a autenticação para vários produtos SaaS com milhares de usuários. O framework implementa gerenciamento seguro de sessão, proteção CSRF e segue as diretrizes da OWASP. No entanto, sempre audite sua implementação específica e mantenha as dependências atualizadas.
Conclusão
Construir autenticação do zero não é mais necessário com soluções modernas como a Better Auth API. Neste guia, criamos um sistema de autenticação completo — do esquema do banco de dados aos componentes da UI — em poucos minutos! A combinação da flexibilidade da Better Auth, da segurança de tipos do Drizzle ORM e do desempenho do Bun cria uma experiência de desenvolvedor que escala do protótipo à produção.
O processo passo a passo demonstra que a autenticação, embora crítica, não precisa ser complexa. Ao aproveitar a arquitetura de plugins e o design agnóstico de framework da Better Auth, você pode se concentrar na construção de recursos que importam para seus usuários, em vez de lutar com implementações de segurança.
Quer você esteja construindo um projeto pessoal ou uma aplicação empresarial, a Better Auth API fornece a base para uma autenticação segura e escalável que se adapta às suas necessidades — e não o contrário.
Quer uma plataforma integrada e completa para sua Equipe de Desenvolvedores trabalhar com máxima produtividade?
Apidog atende a todas as suas demandas e substitui o Postman por um preço muito mais acessível!
