Repository navigation
feat: add database package with Drizzle and Better Auth schema - #1
Merged
Merged
Conversation
Add `packages/db` as the home for the schema, the Drizzle client, and the migration history, with authentication and tenancy owned by Better Auth's organization, admin, and two-factor plugins rather than reimplemented. Organizations are the tenant boundary, so they exist from the first migration instead of being retrofitted around later. The schema is maintained by hand and guarded by tests rather than regenerated into the tree: `auth.test.ts` checks it structurally against what Better Auth reports it writes, and `migrations.test.ts` applies the checked-in SQL to PostgreSQL through PGlite and exercises the constraints it creates. CI runs both, so a Better Auth upgrade cannot pass without executing them. Identifiers are prefixed, generated by the application, and enforced per table by a CHECK constraint. Deliberate strengthenings over Better Auth's generated schema — timestamptz, unique memberships, unique account identities, and a foreign key on the active organization — are recorded in `schema/auth.ts` and ADR 0001.
This was referenced Sep 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
packages/db— the Drizzle schema, client, and migration history — with authentication and tenancy owned by Better Auth rather than reimplemented. Organizations are the tenant boundary (TENANT-01), so they exist from the first migration instead of being retrofitted around later.Decisions are recorded in ADR 0001 (why Drizzle + Better Auth, which plugins, what the tests do and do not prove) and ADR 0002 (identifier format).
docs/data-model.mddescribes the resulting model;docs/security.mdgains the tenant-authorization guardrail.Schema
Eight tables from Better Auth's
organization,admin, andtwoFactorplugins. Teams and dynamic access control are off — both add tables and neither has a requirement yet.The Drizzle schema is maintained by hand rather than generated into the tree, because generated files stop being reviewed. Four deviations deliberately strengthen what Better Auth generates:
timestamptzthroughoutUNIQUE (organization_id, user_id)UNIQUE (provider_id, account_id)session.active_organization_idIdentifiers
Prefixed, application-generated, lowercase base36 —
usr_v1stgxr8z5jdhi6b.packages/db/id.tsis the only place they are generated, and each table enforces its own format in the database:That makes the convention an invariant rather than a habit: a writer not wired to
id.ts— most importantly a Better Auth config that forgetsgenerateId— fails on its first insert instead of quietly storing a second format.Boundaries
createDatabasetakes apgClientorPool, not a connection string, so connection lifecycle stays with the deployment (ARCH-01) — a long-lived pool on a server, a per-request client behind Hyperdrive on Workers:No Drizzle
relations()are declared. They serve Better Auth's opt-in join mode and Drizzle's relational queries; neither is in use, so they are not maintained unused.Verification
bun run check,bun run test(72 passing), andreuse lintall pass. CI now runscheckandtest— previously tests were not a gate, so a Better Auth upgrade could go green without executing the drift guard.schema/auth.test.ts— structural check againstgetAuthTables(), so an upgrade that adds or renames a field fails in CI.schema/migrations.test.ts— applies the checked-in SQL to real PostgreSQL via PGlite (no service container) and asserts the constraints actually reject: wrong-prefix and uppercase IDs, duplicate memberships, duplicate account identities.Not verified: no application consumes this yet, and nothing automatically compares column types, uniqueness, indexes, or references against Better Auth's definitions — ADR 0001 records that gap and the upgrade procedure that covers it.