Guides

Better Auth

Define auth tables with Qubu and connect Better Auth to a transactional Qubu client.

Install the integration

Install the integration next to Qubu and Better Auth:

pnpm add qubu @qubu/better-auth better-auth

Define the auth schema

Define the Better Auth options once. Qubu derives its schema from Better Auth’s resolved public metadata, including:

  • Core tables and renamed models or fields.
  • Additional fields and plugin tables.
  • References and unique constraints.
  • Compound indexes.
import { betterAuth } from "better-auth"
import type { BetterAuthOptions } from "better-auth/types"
import { twoFactor } from "better-auth/plugins"
import { betterAuthSchema, qubuAdapter } from "@qubu/better-auth"
import { qubu } from "qubu"
import { pgAdapter } from "@qubu/adapter-pg"

const options = {
  user: {
    modelName: "auth_users",
    additionalFields: {
      locale: { type: "string", required: false },
    },
  },
  plugins: [twoFactor()],
} satisfies BetterAuthOptions

const authSchema = betterAuthSchema(options, "postgresql")
const db = qubu(pgAdapter(pool))

export const auth = betterAuth({
  ...options,
  database: qubuAdapter(db, { schema: authSchema }),
})

authSchema is an ordinary Qubu Schema. Hand it to Qubu snapshot, diff, migration-plan, and DDL workflows. The adapter's Better Auth createSchema hook emits a TypeScript module that reconstructs the same Qubu-owned metadata.

Database requirements

The package never imports PostgreSQL, MySQL, or SQLite drivers. It executes through Qubu's query and transaction boundaries. PostgreSQL and SQLite use one limited mutation statement for atomic consume and guarded increment operations; MySQL locks one selected row inside the Qubu-owned transaction. A client without transaction support, or a dialect other than PostgreSQL, MySQL, or SQLite, is rejected during adapter construction.

Enum limitation

Better Auth enum metadata is currently rejected because Qubu cannot preserve the closed value set as a portable column without adding a database constraint. The error includes the model and field path instead of silently widening it to text.