# Better Auth conventions ## One server instance - Create a single `export const auth = betterAuth({ … })` in `lib/auth.ts`. It's the source of truth for sessions, providers, and plugins. - The auth instance is **server-only** — never import it into client components or ship it to the browser. ## Configuration & secrets - Read secrets from the environment: `BETTER_AUTH_SECRET`, `BETTER_AUTH_URL`. Never hardcode them. - Enable auth methods explicitly (`emailAndPassword`, `socialProviders.*`), and keep provider client IDs/secrets in env. ## Database - Use a database adapter that matches your stack (`drizzleAdapter`, `prismaAdapter`, …). - Generate the auth schema with `npx @better-auth/cli generate`, then apply it through your own migration tool. Don't hand-edit the generated auth tables. ## Mounting & client - Mount the handler once. In Next App Router: `app/api/auth/[...all]/route.ts` exporting `toNextJsHandler(auth)`. - Build the client with `createAuthClient()` from `better-auth/react`. For each server-side plugin, add its client counterpart so types and calls line up. - `nextCookies()` must be the **last** plugin in the array so cookies are set from server actions. ## Sessions - On the server, read the session with `auth.api.getSession({ headers: await headers() })`. Pass the request headers — don't parse cookies manually. - On the client, use the auth client's `useSession()` / `getSession()`. ## Extending - Add capabilities (`twoFactor`, `passkey`, `organization`, `magicLink`, `admin`, …) through official plugins instead of bespoke auth code. ## Anti-patterns - ❌ Importing the server `auth` instance into client code. - ❌ Hardcoded secrets instead of `BETTER_AUTH_SECRET` / env. - ❌ Reading/parsing the session cookie by hand instead of `auth.api.getSession`. - ❌ `nextCookies()` placed anywhere but last. - ❌ Hand-writing auth tables instead of the CLI-generated schema.