Skip to content

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.