Docs

Next.js integration

Better Auth can be easily integrated with Next.js. It'll also comes with utilities to make it easier to use Better Auth with Next.js.

Before you start, make sure you have a Better Auth instance configured. If you haven't done that yet, check out the installation.

Create API Route

We need to mount the handler to an API route. Create a route file inside /api/auth/[...all] directory. And add the following code:

api/auth/[...all]/route.ts
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";
 
export const { GET, POST } = toNextJsHandler(auth.handler);

You can change the path on your better-auth configuration but it's recommended to keep it as /api/auth/[...all]

For pages route, you need to use toNodeHandler instead of toNextJsHandler and set bodyParser to false in the config object. Here is an example:

pages/api/auth/[...all].ts
import { toNodeHandler } from "better-auth/node"
import { auth } from "@/lib/auth"
 
// Disallow body parsing, we will parse it manually
export const config = { api: { bodyParser: false } }
 
export default toNodeHandler(auth.handler)

Create a client

Create a client instance. You can name the file anything you want. Here we are creating client.ts file inside the lib/ directory.

auth-client.ts
import { createAuthClient } from "better-auth/react" // make sure to import from better-auth/react
 
export const authClient =  createAuthClient({
    //you can pass client configuration here
})

Once you have created the client, you can use it to sign up, sign in, and perform other actions. Some of the actions are reactive. The client use nano-store to store the state and re-render the components when the state changes.

The client also uses better-fetch to make the requests. You can pass the fetch configuration to the client.

RSC and Server actions

The api object exported from the auth instance contains all the actions that you can perform on the server. Every endpoint made inside Better Auth is a invocable as a function. Including plugins endpoints.

Example: Getting Session on a server action

server.ts
import { auth } from "@/lib/auth"
import { headers } from "next/headers"
 
const someAuthenticatedAction = async () => {
    "use server";
    const session = await auth.api.getSession({
        headers: await headers()
    })
};

Example: Getting Session on a RSC

import { auth } from "@/lib/auth"
import { headers } from "next/headers"
 
export async function ServerComponent() {
    const session = await auth.api.getSession({
        headers: await headers()
    })
    if(!session) {
        return <div>Not authenticated</div>
    }
    return (
        <div>
            <h1>Welcome {session.user.name}</h1>
        </div>
    )
}

Server Action Cookies

When you call a function that needs to set cookies, like signInEmail or signUpEmail in a server action, cookies won’t be set. This is because server actions need to use the cookies helper from Next.js to set cookies.

To simplify this, you can use the nextCookies plugin, which will automatically set cookies for you whenever a Set-Cookie header is present in the response.

auth.ts
import { betterAuth } from "better-auth";
import { nextCookies } from "better-auth/next-js";
 
export const auth = betterAuth({
    //...your config
    plugins: [nextCookies()] // make sure this is the last plugin in the array
})

Now, when you call functions that set cookies, they will be automatically set.

"use server";
import { auth } from "@/lib/auth"
 
const signIn = async () => {
    await auth.api.signInEmail({
        body: {
            email: "[email protected]",
            password: "password",
        }
    })
}

Middleware

In Next.js, middleware doesn’t have access to many Node APIs, so you can’t use the usual auth instance to validate sessions directly. Instead, you can make a request to the API route to get the session using the request headers.

Here’s how it looks:

We're using better-fetch to make the request to the API route. You can use any fetch library you want.

import { betterFetch } from "@better-fetch/fetch";
import type { auth } from "@/lib/auth";
import { NextResponse, type NextRequest } from "next/server";
 
type Session = typeof auth.$Infer.Session;
 
export default async function authMiddleware(request: NextRequest) {
	const { data: session } = await betterFetch<Session>(
		"/api/auth/get-session",
		{
			baseURL: request.nextUrl.origin,
			headers: {
				//get the cookie from the request
				cookie: request.headers.get("cookie") || "",
			},
		},
	);
 
	if (!session) {
		return NextResponse.redirect(new URL("/sign-in", request.url));
	}
	return NextResponse.next();
}
 
export const config = {
	matcher: ["/dashboard"],
};

On this page