Better Auth Adapter
Use Better Auth with Jazz as the database adapter.
The Jazz Better Auth adapter lets Better Auth store its tables in Jazz. For general Better Auth setup (route handlers, client, plugins), see the Better Auth documentation.
Schema Workflow
Better Auth tables live in a generated schema-better-auth/ module that your app schema spreads into its own table map. This keeps Better Auth's tables in the same Jazz app as your own data, so a single backend context handles both.
# Generate the Better Auth schema module into schema-better-auth/
npx @better-auth/cli@latest generate \
--config ./src/lib/auth.ts \
--output ./schema-better-auth/schema.ts
# Validate the generated schema source alongside your own
pnpm dlx jazz-tools@alpha validateImport the generated tables in your app's schema.ts and merge them into the app definition:
import { schema as s } from "jazz-tools";
import { schema as betterauthSchema } from "./schema-better-auth/schema";
const schema = {
...betterauthSchema,
messages: s.table({
author_name: s.string(),
chat_id: s.string(),
text: s.string(),
sent_at: s.timestamp(),
}),
// ...your own tables
};
type AppSchema = s.Schema<typeof schema>;
export const app: s.App<AppSchema> = s.defineApp(schema);The generated schema-better-auth/schema.ts also ships a permissions export that denies all operations on the Better Auth tables. Merge it with your own permissions so regular client sessions can't read or write Better Auth rows — the adapter itself uses context.asBackend(), which authenticates with backendSecret and bypasses permission checks entirely.
Database Adapter
Create a server-side Jazz context, then pass it to jazzAdapter(...) as the database in your Better Auth config. Point both db and schema at the merged app — not at the generated module directly — so Better Auth and your own tables share a single Jazz app.
import { betterAuth } from "better-auth";
import { createJazzContext } from "jazz-tools/backend";
import { jazzAdapter } from "jazz-tools/better-auth-adapter";
import { app } from "../../schema";
const jazzContext = createJazzContext({
appId: process.env.APP_ID!,
driver: { type: "memory" },
serverUrl: process.env.SYNC_SERVER_URL!,
env: process.env.NODE_ENV === "production" ? "prod" : "dev",
userBranch: "main",
backendSecret: process.env.BACKEND_SECRET!,
});
export const auth = betterAuth({
database: jazzAdapter({
db: () => jazzContext.asBackend(app),
schema: app.wasmSchema,
}),
// ...your Better Auth config
});| Option | Description |
|---|---|
db | A function returning a Db handle via context.asBackend(app). Use the merged app, not authSchema. |
schema | Typically app.wasmSchema from your merged schema. A raw s.App is also accepted. |
debugLogs | Better Auth adapter debug logging controls. |
usePlural | Whether Better Auth model names should use plural table names. |
prefix | Table name prefix (defaults to "better_auth_"). |
Leave Better Auth joins disabled
The Jazz adapter does not support Better Auth experimental joins yet, so do not enable
experimental.joins.
Publishing to a sync server
Because Better Auth tables are merged into your app schema, they ride on the same deploy as everything else — no separate push for schema-better-auth/. Publish schema, permissions, and migrations through the normal workflow:
pnpm dlx jazz-tools@alpha deploy <appId>See Migrations for the full deploy flow and how schema changes produce migration edges.
Compatibility
The adapter is currently aligned with Better Auth 1.5.5.
| Plugin/Feature | Compatibility |
|---|---|
| Email & Password auth | ✅ |
| Social Provider auth | ✅ |
| Email OTP | ✅ |
Compatibility scope
This section reflects the adapter behavior currently covered by this repo's Better Auth
integration and tests. If you add plugins that introduce extra tables or custom schema fields,
regenerate schema-better-auth/schema.ts and re-run the Jazz schema workflow.