Skip to content

feat: add the server app and wire Better Auth to the schema - #2

Merged
quality-runtime[bot] merged 1 commit into
mainfrom
feat/server-auth
Sep 18, 2026
Merged

quality-runtime[bot] merged 1 commit into
mainfrom
feat/server-auth

Conversation

@quality-runtime

Copy link
Copy Markdown
Contributor

Adds apps/server, a Hono application that mounts Better Auth at /api/auth/*. This is the missing half of #1, which landed the schema but had nothing that could exercise it.

The thing this proves

packages/db could only check the schema structurally — it compared metadata and ran hand-written inserts. Nothing had ever shown that Better Auth itself can write these tables, which mattered because the identifier CHECK constraints reject any insert whose id isn't generated through packages/db/id.ts. A misconfigured generateId would fail every write.

apps/server/auth.test.ts now runs real flows — credential sign-up, organization creation — against migrated PostgreSQL via PGlite, asserting usr_…, acc_…, ses_…, org_…, mem_… rows land. It also initializes the Drizzle adapter, so Better Auth's own start-up schema check runs in CI. I confirmed that check fails loudly rather than passing silently by breaking the schema deliberately: it throws SchemaMismatchError.

Verification, invitation, and two-factor writes are not exercised; the test header says so.

One source of truth for the auth configuration

authOptions in apps/server/auth.ts is the static Better Auth configuration, and the compatibility test derives its expected tables from that same object:

describe.each(Object.entries(getAuthTables(authOptions)))(...)

Previously packages/db declared its own plugin list to test against, which had to be kept in sync by hand. That structural comparison moved here with the configuration, so packages/db no longer depends on better-auth at all — it holds the persistence schema Better Auth expects, while the configuration lives with the app.

Security and dependency decisions

createAuth throws on a secret under 32 characters. Better Auth only logs a warning below that length, so a short secret would otherwise reach production behind a log line. The guard sits in createAuth, not the Bun entry, so a future Workers adapter inherits it.

better-auth and @better-auth/drizzle-adapter are pinned to exact versions. Compatibility with a hand-maintained schema is version-specific and nothing automatically compares column types or constraints against the library, so an upgrade must be a deliberate change rather than something bun update performs. ADR 0001 records the procedure.

appName: "Quality Runtime" is set because it is the TOTP issuer authenticator apps display, and twoFactor() is enabled.

Deployment boundary

The Bun entry is named bun.ts, not index.ts, because it is deployment-specific: it reads the environment and owns a long-lived Pool. A Workers entry would sit beside it and build a per-request Client behind Hyperdrive, which is the shape Cloudflare documents. createApp/createAuth take their dependencies, so neither knows which one it is running under.

Verification

bun run check, bun run test (69 passing), and reuse lint pass. bun run dev boots and serves the routes.

Not verified: no end-to-end run against a standalone PostgreSQL — the integration tests use PGlite, and bun run dev reaches a real server but stops at 3D000 (database does not exist) in this environment.

Add `apps/server`: a Hono application that mounts Better Auth at `/api/auth/*`, plus the Bun entry point that owns the connection pool. `authOptions` is now the single source of truth for the static Better Auth configuration, and the compatibility test derives its expectations from that same object rather than a second plugin list.
This proves what `packages/db` could only assert structurally: Better Auth's own writes satisfy the prefixed-identifier CHECK constraints, and its adapter accepts the schema. The structural comparison moved here with the configuration, so `packages/db` no longer depends on Better Auth at all.
`createAuth` rejects a secret shorter than 32 characters, which Better Auth only warns about, and every deployment adapter inherits that guard. `better-auth` and its Drizzle adapter are pinned exactly, because compatibility with a hand-maintained schema is version-specific and upgrading it is a deliberate change.
@quality-runtime
quality-runtime Bot merged commit 064e341 into main Sep 18, 2026
3 checks passed
@quality-runtime
quality-runtime Bot deleted the feat/server-auth branch September 18, 2026 01:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants