CapyDB Docs
GuidesImports & Migrations

Auth systems and your migration

What your auth system means for a move to CapyDB - Clerk, BetterAuth, Auth.js, Lucia, and Supabase Auth each change the plan differently.

CapyDB is plain Postgres: there is no auth schema, no auth.uid(), no PostgREST enforcing RLS per request. Whether that matters - and how much work it creates - depends entirely on where your identity system lives. capydb migrate scan detects which of these you are.

Clerk (or any external IdP): the best case

Your identity lives outside the database, so the database move cannot break login. Two things to check:

  1. User rows. The common pattern keeps a users table in your own schema synced from Clerk webhooks - that table rides the dump untouched.
  2. RLS policies keyed on the provider JWT (auth.jwt()->>'sub' via PostgREST). These are dropped during import and are inert on plain Postgres anyway. Move that authorization into your server-side data layer: enumerate every policy (select * from pg_policies - the import preflight lists the affected tables for you), and give each one a named guard in the module that owns that table's queries. App-layer guards are easier to test than RLS and work through connection poolers.

If several services share one database credential, RLS was also defense-in-depth across all of them. In that case keep a simple tenant-scoping policy keyed on a per-transaction session setting (SET LOCAL app.org_id = ...) in addition to the app guards - and note it must be set per-transaction to survive transaction pooling.

BetterAuth, Auth.js (NextAuth), Lucia: nothing to do

These store users, sessions, and accounts in your application schema - the dump brings your entire auth state along. Checks:

  • Confirm their tables live in a schema the import carries (public or one the preflight lists as an app schema).
  • JWT-session mode (Auth.js) has no database coupling at all.
  • BetterAuth secondary storage (Redis, if configured) is outside the database and unaffected.
  • Cookie domains and *_URL env vars don't change - only DATABASE_URL does.

Supabase Auth: migrate auth first

If your app calls supabase.auth.* for sign-in and your tables have foreign keys into auth.users, Supabase Auth is your identity system. A database-only migration strands it: the auth schema does not come along, those FKs have no target, and every login path breaks.

The order that works:

  1. Migrate identity to an external provider (Clerk is the well-trodden path) or a DB-native library (BetterAuth), including re-pointing auth.users FKs at your own users table.
  2. Re-run capydb migrate scan - your app is now one of the easy cases above.
  3. Then do the database move.

The import preflight sizes this for you before you commit to anything: it reports every FK into provider-managed schemas and every table with auth.*()-bound RLS.