Skip to content

Join the Seedly owners community →

Authentication

Setting Up Better Auth

Install Better Auth in a Next.js app and connect it to your own database

Written by 12 min read1 activity
Buzz, your presenter

Buzz presents

one install, one secret, one little auth file, and a few more steps. we set up Better Auth together, no worries at all!one install, one secret, one little auth file, and a few more steps. we set up Better Auth together, no worries at all!

Buzz finishes building a wooden birdhouse kit next to a brass key and coiled cord
A few pieces fit together to set up Better Auth

Time to actually set up Better Auth in a Next.js app, following the steps from the official installation guide. By the end you'll have a secret key, an auth file, database tables, a route that handles login requests and a client your pages can call. Sounds like a lot... it's mostly copy and paste.

Before You Start#

You need two things ready to go.

  • A Next.js project using the App Router (the app/ folder)
  • A database you can connect to, such as PostgreSQL, MySQL or SQLite

Everything below matches the Better Auth installation guide. The commands are shown with npm, and the docs list the pnpm, yarn and bun versions too.

Installing Better Auth#

Step 1. Install the Package#

In your project folder, run this.

npm install better-auth

That adds Better Auth to your project, same as any other npm package.

Step 2. Set Your Environment Variables#

Make a .env file in the root of your project. Better Auth needs two values in there.

BETTER_AUTH_SECRET=
BETTER_AUTH_URL=http://localhost:3000

BETTER_AUTH_SECRET is a secret value used for encryption and hashing. The docs say it has to be at least 32 characters and generated with high entropy, which is a fancy way of saying truly random. The installation page has a button that generates one for you, or you can run this in your terminal.

openssl rand -base64 32

Paste whatever it spits out right after the equals sign.

BETTER_AUTH_URL is the base URL of your app. While you're building locally, that's http://localhost:3000. Once you go live, it becomes your real website address.

Step 3. Create the Auth Instance#

Next up, create a file named auth.ts. The docs say it can live in the project root, a lib/ folder or a utils/ folder, and those folders can sit inside src/, app/ or server/. This lesson goes with lib/auth.ts.

// lib/auth.ts
import { betterAuth } from "better-auth";
 
export const auth = betterAuth({
  //...
});

The docs say to export it with the name auth or as a default export. This auth object is the server side of Better Auth, and everything else plugs into it.

Step 4. Connect Your Database#

Better Auth keeps users and sessions in your database, so you have to tell it how to reach that database. Here's the PostgreSQL version, based on the example from the PostgreSQL adapter page.

This example uses the pg package to talk to PostgreSQL, so install that first, along with its TypeScript types.

npm install pg
npm install -D @types/pg

Then add your database's connection string to that same .env file as DATABASE_URL. Swap in the user, password, host and database name for your own.

DATABASE_URL=postgres://user:password@localhost:5432/database
// lib/auth.ts
import { betterAuth } from "better-auth";
import { Pool } from "pg";
 
export const auth = betterAuth({
    database: new Pool({
        connectionString: process.env.DATABASE_URL,
    }),
})

The connection string gets read from DATABASE_URL, so the password stays in .env and OUT of your code. The adapter page's own example pastes the string straight in. That's fine for a quick test, but it's really easy to commit by accident (and once a password lands somewhere public, the only fix is changing it).

The docs show matching examples for SQLite (using better-sqlite3) and MySQL (using mysql2/promise). If you already use an ORM, there are built-in adapters for that instead. Prisma users import prismaAdapter from better-auth/adapters/prisma, for example, and Drizzle users import drizzleAdapter from better-auth/adapters/drizzle.

Step 5. Turn On a Sign In Method#

Now tell Better Auth how people get in. Keep it simple and start with email and password.

// lib/auth.ts
import { betterAuth } from "better-auth";
import { Pool } from "pg";
 
export const auth = betterAuth({
    database: new Pool({
        connectionString: process.env.DATABASE_URL,
    }),
    emailAndPassword: {
        enabled: true,
    },
})

You can add social providers the same way later on. The docs show GitHub added under socialProviders, using a client ID and secret stored in your environment variables.

Step 6. Create the Database Tables#

Better Auth ships with a command line tool (CLI) that already knows which tables it needs. There are two commands.

Generate creates a schema file or SQL migration file for you to apply.

npx auth@latest generate

Migrate creates the tables directly in your database.

npx auth@latest migrate

So which one do you use? The docs say migrate is only available for the built-in Kysely adapter. The PostgreSQL adapter page explains that PostgreSQL "is supported under the hood via the Kysely adapter", which means the direct Pool setup above is using it. That's why the same page lists both generate and migrate as supported, and migrate is the quick option here. If you're on Prisma or Drizzle, run generate and then apply the change with your ORM's own migration tool.

Connecting It to Next.js#

Step 7. Mount the Route Handler#

Your app needs a URL that receives login requests. By default Better Auth uses paths that start with /api/auth/. In the App Router, create this file.

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

That [...all] folder name is a Next.js catch-all route. It sends every request under /api/auth/ to Better Auth, so sign up, sign in and sign out ALL run through this one file.

Step 8. Create the Client Instance#

Last piece. The client is what your pages use to talk to the route you just made. For React and Next.js, import it from better-auth/react.

// lib/auth-client.ts
import { createAuthClient } from "better-auth/react"
export const authClient = createAuthClient({
    /** The base URL of the server (optional if you're using the same domain) */
    baseURL: "http://localhost:3000"
})

The docs say you can skip baseURL when your auth server runs on the same domain as your pages, which is the usual setup in a Next.js app.

What You Built#

Buzz pours colourful marbles from a cloth bag into a small brass lockbox
A long random secret keeps your auth safe

Here's how all the pieces fit together.

FileJob
.envHolds the secret, the base URL and the database connection string
lib/auth.tsThe server side setup, including the database and sign in methods
app/api/auth/[...all]/route.tsReceives login requests at /api/auth/
lib/auth-client.tsLets your pages sign users up, in and out

TL;DR#

  • Install with npm install better-auth.
  • Set BETTER_AUTH_SECRET (at least 32 random characters) and BETTER_AUTH_URL in .env.
  • Create auth.ts with betterAuth, connect your database, and turn on emailAndPassword.
  • Create tables with npx auth@latest generate or, for the built-in Kysely adapter, npx auth@latest migrate.
  • Mount toNextJsHandler(auth) at app/api/auth/[...all]/route.ts.
  • Create the client with createAuthClient from better-auth/react.

What's Next?#

Everything's wired up. In the next lesson you'll use authClient to sign users up, sign them in and out, read the session and protect a page.

This lesson ends with a short activity.