BetterAuth Setup (80-20)#
The 20% you need to know that covers 80% of use cases.
Installation#
npm install better-auth
Database Setup#
BetterAuth needs a database. Generate schema:
npx better-auth generate
This creates migration files for your database.
Basic Configuration#
// lib/auth.ts
import { betterAuth } from "better-auth"
export const auth = betterAuth({
database: {
provider: "pg", // or "mysql", "sqlite"
url: process.env.DATABASE_URL,
},
// Enable email/password auth
emailAndPassword: {
enabled: true,
requireEmailVerification: true,
},
// Session config
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 days
updateAge: 60 * 60 * 24, // Update every 24h
},
})
API Routes#
Next.js App Router#
// app/api/auth/[...all]/route.ts
import { auth } from "@/lib/auth"
export const { GET, POST } = auth.handler
Next.js Pages Router#
// pages/api/auth/[...all].ts
import { auth } from "@/lib/auth"
export default auth.handler
Client Setup#
// lib/auth-client.ts
import { createAuthClient } from "better-auth/client"
export const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_APP_URL,
})
Usage in Components#
Sign Up#
import { authClient } from "@/lib/auth-client"
async function handleSignUp(email: string, password: string, name: string) {
const { data, error } = await authClient.signUp.email({
email,
password,
name,
})
if (error) {
console.error(error.message)
return
}
console.log("User created:", data.user)
}
Sign In#
async function handleSignIn(email: string, password: string) {
const { data, error } = await authClient.signIn.email({
email,
password,
})
if (error) {
console.error(error.message)
return
}
console.log("Signed in:", data.user)
}
Sign Out#
async function handleSignOut() {
await authClient.signOut()
}
Get Session#
import { authClient } from "@/lib/auth-client"
// Client-side
const session = await authClient.getSession()
if (session?.user) {
console.log("Logged in as:", session.user.email)
}
Server-Side Session#
import { auth } from "@/lib/auth"
import { headers } from "next/headers"
export async function getServerSession() {
const session = await auth.api.getSession({
headers: headers(),
})
return session
}
Protected Routes (Middleware)#
// middleware.ts
import { auth } from "@/lib/auth"
import { NextResponse } from "next/server"
export async function middleware(request: Request) {
const session = await auth.api.getSession({
headers: request.headers,
})
if (!session) {
return NextResponse.redirect(new URL("/login", request.url))
}
return NextResponse.next()
}
export const config = {
matcher: ["/dashboard/:path*", "/profile/:path*"],
}
Email Verification#
// Send verification email
await authClient.sendVerificationEmail({
email: "user@example.com",
})
// Verify email
await authClient.verifyEmail({
token: "verification-token",
})
OAuth Providers#
Google OAuth#
export const auth = betterAuth({
// ... other config
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
},
},
})
Sign in with Google (Client)#
await authClient.signIn.social({
provider: "google",
callbackURL: "/dashboard",
})
Common Patterns#
Check if User is Logged In#
const { data: session } = await authClient.getSession()
const isLoggedIn = !!session?.user
Get Current User#
const { data: session } = await authClient.getSession()
const user = session?.user
if (user) {
console.log(user.email, user.name)
}
Update User#
await authClient.updateUser({
name: "New Name",
})
TypeScript Types#
import type { Session, User } from "better-auth"
// Session type
const session: Session = {
user: {
id: "123",
email: "user@example.com",
name: "John Doe",
},
// ... other fields
}
Error Handling#
const { data, error } = await authClient.signIn.email({
email,
password,
})
if (error) {
switch (error.status) {
case 400:
console.log("Invalid credentials")
break
case 429:
console.log("Too many attempts")
break
default:
console.log("An error occurred")
}
}
Best Practice
Always check for error before accessing data to ensure type safety and proper error handling.