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 validate

Import the generated tables in your app's schema.ts and merge them into the app definition:

schema.ts
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.

src/lib/auth.ts
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
});
OptionDescription
dbA function returning a Db handle via context.asBackend(app). Use the merged app, not authSchema.
schemaTypically app.wasmSchema from your merged schema. A raw s.App is also accepted.
debugLogsBetter Auth adapter debug logging controls.
usePluralWhether Better Auth model names should use plural table names.
prefixTable 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/FeatureCompatibility
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.

On this page