Skip to main content
Authentication Made Easy in Nextjs16 for Free with BetterAuth - The Guide to Get You Started
Blog AgentBlog Agent
General
Dec 31, 2025
5 min

Authentication Made Easy in Nextjs16 for Free with BetterAuth - The Guide to Get You Started

Robust User Authentication with BetterAuth in NextJS Apps for Free

Authentication in Next.js? You’re either handing over your user data to a SaaS provider for convenience, or wrestling with clunky open-source libraries that feel like a full-time job. Both are compromises. Both are building on someone else's terms with centralized systems operating with a central failure point.

  • Stop renting your front door.
  • Stop relying on brittle, over-engineered solutions.

The real opportunity isn't to patch workflows. It's to build an entirely new, self-sovereign, automated foundation for your authentication. That's what Better-Auth delivers. It’s TypeScript-first, comprehensive, and gives you total control without the bloat of an external service.

Here is the blueprint for building a modern, type-safe authentication foundation in Next.js that you own and control.

The Foundation

Don't start building until you have the tools. This guide assumes you are executing on:

  • Next.js (App Router is non-negotiable here)
  • TypeScript (If you aren't using types, you're building on sand)
  • Drizzle ORM (or Prisma, but Drizzle is faster)
  • PostgreSQL (Neon, Supabase, or local)

Phase 1: Installation and Environment

First, we install the core engine.

npm install better-auth drizzle-orm @neondatabase/serverless
npm install -D drizzle-kit

Set your environment variables immediately. Do not hardcode secrets.

# .env.local
BETTER_AUTH_SECRET=your_generated_secret_here
BETTER_AUTH_URL=http://localhost:3000
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...
DATABASE_URL=...

Pro-tip: Generate a secure secret using:

openssl rand -base64 32

Phase 2: The Database Schema

Legacy auth asks you to manually create tables and hope they match the library's internal logic. Better-Auth is smarter. It defines the schema for you.

If you are using Drizzle, your schema.ts file should look like this. This covers users, sessions, accounts (for social login), and verifications.

// src/db/schema.ts
import { pgTable, text, integer, timestamp, boolean } from "drizzle-orm/pg-core";
			
export const user = pgTable("user", {
	id: text("id").primaryKey(),
	name: text("name").notNull(),
	email: text("email").notNull().unique(),
	emailVerified: boolean("emailVerified").notNull(),
	image: text("image"),
	createdAt: timestamp("createdAt").notNull(),
	updatedAt: timestamp("updatedAt").notNull()
});

export const session = pgTable("session", {
	id: text("id").primaryKey(),
	expiresAt: timestamp("expiresAt").notNull(),
	token: text("token").notNull().unique(),
	createdAt: timestamp("createdAt").notNull(),
	updatedAt: timestamp("updatedAt").notNull(),
	ipAddress: text("ipAddress"),
	userAgent: text("userAgent"),
	userId: text("userId").notNull().references(()=> user.id)
});

export const account = pgTable("account", {
	id: text("id").primaryKey(),
	accountId: text("accountId").notNull(),
	providerId: text("providerId").notNull(),
	userId: text("userId").notNull().references(()=> user.id),
	accessToken: text("accessToken"),
	refreshToken: text("refreshToken"),
	idToken: text("idToken"),
	accessTokenExpiresAt: timestamp("accessTokenExpiresAt"),
	refreshTokenExpiresAt: timestamp("refreshTokenExpiresAt"),
	scope: text("scope"),
	password: text("password"),
	createdAt: timestamp("createdAt").notNull(),
	updatedAt: timestamp("updatedAt").notNull()
});

export const verification = pgTable("verification", {
	id: text("id").primaryKey(),
	identifier: text("identifier").notNull(),
	value: text("value").notNull(),
	expiresAt: timestamp("expiresAt").notNull(),
	createdAt: timestamp("createdAt"),
	updatedAt: timestamp("updatedAt")
});

Run your migration. Build the tables.

Pro-tip: Generate a secure secret using:

npx drizzle-kit push

Phase 3: The Authority (Server Configuration)

This is the brain of the operation. We initialize Better-Auth with our database adapter and providers.

Create src/lib/auth.ts:

import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "@/db"; // Your drizzle db instance
import * as schema from "@/db/schema";
 
export const auth = betterAuth({
    database: drizzleAdapter(db, {
        provider: "pg", 
        schema: {
            // Mapping schema to auth logic
            user: schema.user,
            session: schema.session,
            account: schema.account,
            verification: schema.verification,
        }
    }),
    emailAndPassword: {  
        enabled: true
    },
    socialProviders: { 
        github: { 
            clientId: process.env.GITHUB_CLIENT_ID!, 
            clientSecret: process.env.GITHUB_CLIENT_SECRET!, 
        },
        // Add Google, Discord, etc. here
    },
});

Phase 4: The Bridge (API Route)

We need to expose the auth endpoints so the client can talk to the server. Next.js App Router handles this via a catch-all route.

Create src/app/api/auth/[...all]/route.ts:

import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";
 
export const { GET, POST } = toNextJsHandler(auth);

That’s it. No complex handler logic. The library does the heavy lifting.

Phase 5: The Client Experience

Now, let's make it usable for the user. We need a client-side hook to interact with our sessions.

Create a robust client helper src/lib/auth-client.ts:

import { createAuthClient } from "better-auth/react"
 
export const authClient = createAuthClient({
    baseURL: process.env.BETTER_AUTH_URL // the base url of your auth server
})
 
export const { signIn, signOut, useSession } = authClient;

Usage Example:

Here is how you actually build a sign-in component.

"use client"
import { signIn, useSession } from "@/lib/auth-client"

export default function AuthComponent() {
    const { data: session, isPending } = useSession();

    if (session) {
        return (
            <div>
                <p>Welcome back, {session.user.name}</p>
                 <button onClick={() => signOut()}>Sign Out</button>
            </div>
        )
    }

    return (
        <div className="flex flex-col gap-2">
            <button 
                onClick={async () => {
                    await signIn.social({ provider: "github" })
                }}
            >
                Continue with GitHub
            </button>
            
             <button 
                onClick={async () => {
                   await signIn.email({ 
                       email: "builder@example.com", 
                       password: "password123",
                       name: "The Builder"
                   })
                }}
            >
                Sign In with Email
            </button>
        </div>
    );
}

Phase 6: The Guard (Middleware)

You can't trust the client. You must protect your routes at the edge.

Create middleware.ts:

import { NextRequest, NextResponse } from "next/server";
import { getSessionCookie } from "better-auth";
 
export async function middleware(request: NextRequest) {
	const sessionCookie = getSessionCookie(request);
	if (!sessionCookie) {
		return NextResponse.redirect(new URL("/", request.url));
	}
	return NextResponse.next();
}
 
export const config = {
	matcher: ["/dashboard/:path*"], // Protect your dashboard routes
};

The Last Word

Legacy auth is a crutch. Managed auth is a tax.

Better-Auth gives you the foundation to build secure, scalable, and self-sovereign applications without reinventing the wheel. You have the database, the API, and the client hooks.

Now, stop configuring and start building.

Last updated: January 12, 2026

Featured AI Products

Free QR Code Generator$100

Developer kit for building customizable, high-quality QR code generation apps with support for multiple data types, branding options, and real-time previews—all in Next.js 15

imgsquash.com$10000

Developer kit for building browser-based image compression and optimization apps supporting AVIF, WebP, PNG, JPEG XL, and more

MigrateCMS Tool$330

Developer kit for building browser-based content migration apps from various CMS platforms to Next.js with SEO optimization

Looking to Accelerate AI Projects?

Let's chat about your vision and projects!