Skip to content

Better Auth database

Better Auth’s built-in database layer is Kysely, and any Kysely dialect plugs into it. @persql/kysely is that dialect for PerSQL, so an app’s users, sessions, and accounts can live in their own isolated SQLite database — one per app, or one per tenant.

npm install better-auth @persql/kysely @persql/sdk
import { betterAuth } from "better-auth";
import { PerSQL } from "@persql/sdk";
import { PerSQLDialect } from "@persql/kysely";
const client = new PerSQL({ token: process.env.PERSQL_TOKEN });
export const auth = betterAuth({
database: {
dialect: new PerSQLDialect({ database: client.database("acme/auth") }),
type: "sqlite",
},
emailAndPassword: { enabled: true },
});

Better Auth generates its schema through the same dialect. Run the migration step once at deploy time (or whenever you add plugins that bring tables):

import { getMigrations } from "better-auth/db/migration";
const { runMigrations } = await getMigrations(auth.options);
await runMigrations();

This creates the user, session, account, and verification tables in the PerSQL database, plus whatever your enabled plugins need.

The auth database is a normal PerSQL database: inspect sign-ups from the console, expose a read-only endpoint over it, or answer operational questions with SQL:

SELECT date(createdAt) AS day, COUNT(*) AS signups
FROM user GROUP BY 1 ORDER BY 1 DESC LIMIT 14;

For multi-tenant products, mint a database per tenant and point each tenant’s betterAuth instance at its own: tenant data never shares tables, deleting a tenant is deleting a database, and each tenant’s auth spend is metered against its own namespace.