fix(drizzle-kit): reference authUsers for FKs into Supabase auth.users#5979
Open
dmitriy-bty wants to merge 1 commit into
Open
fix(drizzle-kit): reference authUsers for FKs into Supabase auth.users#5979dmitriy-bty wants to merge 1 commit into
dmitriy-bty wants to merge 1 commit into
Conversation
When pulling a Supabase database, foreign keys that target the managed
auth.users table were rendered as a reference to an undeclared local
table, because the auth schema is excluded from the schema filter and
never emitted (the FK reference resolves to a bare "users" that is
neither declared nor imported). drizzle-orm already ships this table as
authUsers in the drizzle-orm/supabase entrypoint.
The pg introspection emitter now renders such foreign keys as authUsers
and adds the import { authUsers } from "drizzle-orm/supabase" statement,
but only when auth.users itself was not introspected.
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
Fixes the primary defect in #4482:
drizzle-kit pullagainst a Supabase Postgres databasegenerates a
schema.tsthat does not compile, because every foreign key that referencesSupabase's managed
auth.users.idis emitted as a reference to a local table that was neverdeclared.
drizzle-orm already ships the canonical
auth.usersdefinition asauthUsersin thedrizzle-orm/supabaseentrypoint. The pull emitter simply never referenced it for cross-schemaforeign keys into the
authschema. This PR makes the emitter render such FKs as the importedauthUsersand add the corresponding import.This fixes both files a
pullwrites —schema.tsandrelations.ts— which were broken by thesame root cause.
Before (actual output of
drizzle-kit pull, verified against a live Postgres)After
Root cause
pgSerializer.ts:1275,fnsp.nspname AS foreign_table_schema; surfaced asschemaToatpgSerializer.ts:1317), so the FKcorrectly carries
{ tableTo: 'users', schemaTo: 'auth' }.pullonly introspects the requested schema(s) —publicby default (the schema filter isbuilt in
fromDatabaseatpgSerializer.ts:991,n.nspname = '<schema>'), so theauth.userstable is never emitted as a local declaration.
createTableFKs(drizzle-kit/src/introspect-pg.ts)the reference is built via
paramNameFor(it.tableTo, schemas[it.schemaTo]). Because theauthschema was not pulled it is absent from the
schemasmap, soschemas['auth']isundefinedandparamNameFor('users', undefined)returns the bareusers(theIn<Schema>suffix is only addedfor a defined non-public schema —
paramNameFor,introspect-pg.ts:304-305). The result referencesa
usersvariable that does not exist in the generated file.authUsersis defined atdrizzle-orm/src/supabase/rls.ts:14(auth.table('users', { id: uuid()... }))and re-exported from the
drizzle-orm/supabaseentrypoint — exactly what a Supabase user is expectedto reference.
Implementation
schema.tsemitter (drizzle-kit/src/introspect-pg.ts):isSupabaseAuthUsersFk(fk)→fk.schemaTo === 'auth' && fk.tableTo === 'users'.schemaToTypeScript, computeusesSupabaseAuthUsers: true when at least one FK targetsauth.usersandauth.usersis not itself part of the introspected tables (so users whoexplicitly include the
authschema in their filter keep the previous local-table behavior).createTableFKstakes asupabaseAuthUsersflag; when set and the FK matches, it rendersauthUsers(instead of the bare, undeclaredusers). This Supabase branch is evaluated beforethe existing self-FK check — that check compares table names only, so a local table also named
users(a commonpublic.users→auth.usersmirror) would otherwise be mis-emitted as aself-reference. The reorder is safe because
usesSupabaseAuthUsersis only true whenauth.userswas not emitted, so a referencing table named
usersis necessarilypublic.users, never agenuine self-reference into an emitted
auth.users.import { authUsers } from "drizzle-orm/supabase".relations.tsemitter (drizzle-kit/src/cli/commands/introspect.ts):A full
pullalso writesrelations.tsvia a separate emitter with the same defect — it rendered theFK target as
usersInAuthimportedfrom "./schema", a name the fixedschema.tsno longer declares,so
relations.tsfailed to compile too. Applying the same guarded mapping (identicalauth.usersdetection), it now renders
authUsersand routes that single import todrizzle-orm/supabase, whileevery other table still imports from
./schema.Both changes are additive and guarded, so non-Supabase schemas and schemas that explicitly pull the
authschema are unaffected (byte-identical output).On the detection signal: this keys on the
auth.usersschema/table-name heuristic. drizzle-kitalso exposes an explicit Supabase marker (
roles.provider === 'supabase',pgSerializer.ts:940); Ikept the name heuristic because many Supabase projects don't introspect roles, but I'm happy to gate
on/combine with the provider signal if you'd prefer. The only downside of the pure heuristic is a
non-Supabase database that has its own
auth.usersand is pulled without theauthschema would geta spurious
authUsersimport — a narrow case that does not regress compiling output (that pullalready emitted a dangling
users).Validation
drizzle-kit/tests/introspect/pg.test.ts:introspect FK to Supabase auth.users references authUsers…— seeds anauthschema +auth.usersand apublic.profilestable with a FK intoauth.users(id), introspects only thepublicschema (the real Supabase workflow), and asserts the generated file containsimport { authUsers } from "drizzle-orm/supabase"andforeignColumns: [authUsers.id]and nolonger contains the undeclared
foreignColumns: [users.id]. It also asserts the separately-emittedrelations.tsimportsauthUsersfromdrizzle-orm/supabaseand no longer referencesusersInAuth.…a local table also named "users" is not treated as a self-reference— thepublic.users→auth.usersmirror case; asserts the FK resolves toauthUsers.idand is not collapsed to abogus
foreignColumns: [table.id].drizzle-kit:pullemitsforeignColumns: [users.id]referencing an undeclared, un-importedusers— confirming the issue.Copy-pasteable SQL seed +
pullcommand are in the repro notes.currently blocked by an unrelated upstream issue (
drizzle-kitdev-depends on adrizzle-kitversion whose tarball 404s on npm, and drizzle-kit consumes drizzle-orm as a built
dist). Thechange is therefore verified by code inspection plus the live reproduction above; please run CI /
dprint fmton the added tests. Happy to adjust formatting if the linter flags the reorderedternary.
Adjacent issues noted (not fixed here)
The report lists two further defects that are genuinely separate emitter bugs. I root-caused them but
scoped them out; happy to open follow-ups:
.default([RAY])garbled array default (Update README docs #2 in the report). A separate emitter bug inarray-default rendering (
buildArrayDefault,introspect-pg.ts:657), which strips two charactersfrom each end of curly-form array-literal defaults. The exact input that produces the reported
[RAY]needs confirming against the reporter's column default before I'd propose a fix — noted fora follow-up, not addressed here.
.enableRLS()(Feature/last updates #3 in the report). Introspection captures RLS state(
pgSerializer.ts:1663,isRLSEnabled: row.rls_enabled) but theintrospect-pg.tsemitter has noenableRLS/isRLSEnabledreferences, so the flag is dropped. Fix direction: append.enableRLS()in the table statement builder when
table.isRLSEnabled.