feat(db): replace UUID with prefixed CUID2 IDs#2162
Merged
Conversation
Switch all primary keys from `gen_random_uuid()` to application-generated prefixed CUID2 identifiers (e.g. `usr_ght4k2jxm7pqbv01`). The 3-char prefix encodes entity type for instant recognition in logs, URLs, and support tickets — same pattern as Stripe and Clerk. - Add `db/schema/id.ts` with `generateAuthId` / `generateId` helpers - Wire `generateAuthId` into Better Auth's `advanced.database.generateId` - Migrate all schema files from `.default(sql`gen_random_uuid()`)` to `.$defaultFn(() => generateAuthId(...))` - Regenerate `0000_init` migration (removes DB-level defaults) - Add `@paralleldrive/cuid2` dependency to `db` package - Add spec at `docs/specs/prefixed-ids.md`
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.
Summary
Replace
gen_random_uuid()database defaults with application-generated prefixed CUID2 identifiers across all auth tables. The 3-char prefix encodes entity type for instant recognition in logs, URLs, and support tickets — same pattern as Stripe (cus_,sub_), Clerk (user_,org_).Why prefixed IDs
usr_...in a Sentry trace or log line immediately tells you the entity type without a database lookuporg_ID can reject ausr_ID before hitting the DBWhy CUID2 over UUID v4
Changes
db/schema/id.ts(new) —generateAuthId(model)andgenerateId(prefix)helpers with prefix map, lazy CUID2 initdb/schema/*.ts— replace.default(sql'gen_random_uuid()')with.$defaultFn(() => generateAuthId(...))apps/api/lib/auth.ts— wiregenerateAuthIdinto Better Auth'sadvanced.database.generateIdhookdb/migrations/0000_init.sql— regenerated (removes DB-levelDEFAULTclauses)db/package.json— add@paralleldrive/cuid2dependencydocs/— update schema docs, adddocs/specs/prefixed-ids.mdspecUsage
Prefix map
userusrusersessionsessessionaccountidnidentityverificationvfyverificationorganizationorgorganizationmembermemmemberinvitationinvinvitationpasskeypkypasskeysubscriptionsubsubscriptionMigration
Existing
text()columns accept both UUID and prefixed-CUID2 values — no data migration required. New inserts get the new format; legacy rows keep UUIDs.Test plan
bun typecheckpassesbun testpassesbun db:generateproduces no diff (migration already regenerated)usr_prefixed IDs