bao
Framework guides

Next.js

Add bao authentication to a Next.js app — client setup, protected routes, and server-side session access.

Install

npm install better-auth

Create the auth client

Export a single instance from a shared module. Use NEXT_PUBLIC_AUTH_URL so the client works in both browser and server components.

// lib/auth-client.ts
import { createAuthClient } from "better-auth/client";

export const authClient = createAuthClient({
  baseURL: process.env.NEXT_PUBLIC_AUTH_URL!,
});

Add to .env.local:

NEXT_PUBLIC_AUTH_URL=https://my-app-production.workers.dev

Sign in

// app/login/page.tsx
"use client";
import { authClient } from "@/lib/auth-client";

export default function LoginPage() {
  async function handleLogin(formData: FormData) {
    const { error } = await authClient.signIn.email({
      email: formData.get("email") as string,
      password: formData.get("password") as string,
    });
    if (!error) window.location.href = "/dashboard";
  }

  return (
    <form action={handleLogin}>
      <input name="email" type="email" />
      <input name="password" type="password" />
      <button type="submit">Sign in</button>
    </form>
  );
}

Get the session in a Server Component

// app/dashboard/page.tsx
import { headers } from "next/headers";
import { redirect } from "next/navigation";

export default async function DashboardPage() {
  const res = await fetch(
    `${process.env.NEXT_PUBLIC_AUTH_URL}/api/auth/get-session`,
    { headers: await headers() }
  );
  const session = await res.json();

  if (!session?.user) redirect("/login");

  return <p>Hello {session.user.email}</p>;
}

Protect routes with middleware

// middleware.ts
import { NextResponse, type NextRequest } from "next/server";

const PROTECTED = ["/dashboard"];

export async function middleware(request: NextRequest) {
  const isProtected = PROTECTED.some((path) =>
    request.nextUrl.pathname.startsWith(path),
  );
  if (!isProtected) return NextResponse.next();

  const res = await fetch(
    `${process.env.NEXT_PUBLIC_AUTH_URL}/api/auth/get-session`,
    { headers: request.headers },
  );
  const session = await res.json();

  if (!session?.user) {
    return NextResponse.redirect(new URL("/login", request.url));
  }

  return NextResponse.next();
}

export const config = {
  matcher: ["/dashboard/:path*"],
};

CORS

Add your Next.js origin to bao.config.json — see CORS configuration.