Declarative schema files, ordering, migrations and audit triggers.
Change supabase/schemas/**/*.sql, then run pnpm supabase:diff or supabase db diff -f <name>. Do not write migration SQL by hand. The exceptions are the documented declarative caveats: data changes, column comments, and the supabase_auth_admin grants that must be appended after an init migration is regenerated.
db diff does not emit column-level grants such as grant update (first_name, ...) on public.profiles. A generated migration can still revoke the table-level grant, which leaves the column grant missing in production. Write column grants into a migration by hand and cover them with a check in supabase/tests/database/grants.test.sql.
Files live in domain folders, but the global number in each file name decides when it runs. schema_paths in supabase/config.toml lists every file sorted by file name. When a file depends on another file with the same number, give it the next free number instead of relying on alphabetical order. After any move or rename, pnpm supabase:diff must report no schema changes.
Define each function in exactly one file. With two definitions, the file that runs last wins without any error, and a reorder can silently deploy the wrong body.
db diff compares structure, not rows. Data inserted by a schema file, such as audit_log_table_registry rows, exists only while the diff runs. Anything production needs must end up in DDL. For example, audit column lists travel as trigger arguments.
A new table with organization_id and id gets the *_capture_audit_log trigger automatically. Add ignored columns (timestamps, leases, search vectors) and redacted columns (tokens, secrets, credentials) in platform/067_triggers_audit_log.sql. If a table must not be audited, add it to the exemption list in supabase/tests/database/audit.test.sql with a reason. Then run pnpm supabase:test.
pnpm supabase:types generates public and graphql_public together. Generating public alone drops graphql_public.
Files 012 to 063 in supabase/seeds/ hold platform data (auth users, profiles, roles, catalogs and settings). Files 100 to 170 hold the demo tenants, one domain per file, and each is one do block that starts with a -- Covers: comment. The header of 100_organizations.sql is the registry: the six organizations, every persona and the table codes used in seeded ids.
{TT}{O}00000-0000-4000-8000-{NNNNNNNNNNNN}. Pick a table code that the registry does not list yet and add it there. demo-seeds-static.test.ts fails on an unregistered code.'TT_00000-0000-4000-8000-%'. A bare 'TT%' also matches rows that triggers created with random uuids.TASK-1, Q-8, INV-2026-4), never uuids.111_catalog.sql.set_config('app.audit_retention', 'on', true) and set it back to off.170_integrity.sql runs last. It recomputes allocations, order totals and number sequences, so add new numbered documents before it.supabase/seed-buckets/<bucket>/ and load on pnpm supabase:reset. Columns store the object path only.begin; ... rollback; into docker exec -i supabase_db_CentraKit psql.The Supabase integration records every seed file it runs in supabase_migrations.seed_files. On a push it runs only seed files it has not recorded yet. It skips a file that already ran, even when the file changed. Adding new seed files on top of an older seed set therefore runs them against data they did not expect, and edits to 012 or other earlier files never reach develop.
The seeds must still survive a rerun locally. 012_auth_users.sql deletes the seeded organizations by slug and by id before it deletes the seeded users, with app.skip_allocation_release and app.shipment_transition switched on. Team ids are pinned in 101_teams.sql because later files reference them.
When a seed restructure breaks the Supabase Preview check, reset the develop database. Reset branch in the dashboard does not drop data; it only redeploys.
Reset develop from the CLI. branches get returns the develop connection string with its password, and never returns one for main:
pnpm exec supabase db reset --db-url "$(pnpm exec supabase branches get develop --project-ref gunugimgrrnxqynjghqe -o env | grep '^POSTGRES_URL=' | cut -d= -f2- | tr -d '"')"Never use --linked for this. The local link points at production.
A remote reset drops every object in one transaction and fails with out of shared memory at the default lock limit. Develop runs with max_locks_per_transaction=1024. If a new branch needs it, set it with pnpm exec supabase postgres-config update --experimental --project-ref <ref> --config max_locks_per_transaction=1024, which restarts that database.
Production keeps seeding disabled, so seed changes never need a production reset.
Last updated on