mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Docs update. ## What is the current behavior? Linear: [CLI-1618](https://linear.app/supabase/issue/CLI-1618/update-cli-workflow-docs-for-pg-delta-default-diffing) Four docs pages lag the shipped CLI behavior now that `pg-delta` is the default diff engine for projects created by a recent `supabase init`: - **CLI workflows** claims `db diff` compares `supabase/schemas/` against migrations. Under `pg-delta`, declarative files are never the `db diff` baseline (and `[db.migrations].schema_paths` no longer changes it) — the declarative flow goes through `supabase db schema declarative sync`. The cleanup guidance describes `migra`-era output. - **Declarative database schemas** teaches the old `db diff -f` + `schema_paths` flow throughout, and its known-caveats list is the `migra` issue list. - **Managing environments** still presents `--use-migra` as an "experimental flag" for a "more concise" diff — inverted now. - **Backup and restore (migrating within Supabase)** and the CLI workflows guide both steer users to `db diff`/`db pull` with `--schema auth,storage`. Under `pg-delta`, `--schema` layers an extra exclude policy on top of the Supabase profile: it can only narrow a diff, never re-include managed schemas, and managed-schema selections can even fail closed (e.g. `--schema auth` when a trigger function lives in `public`). Unfiltered diffs are the supported path. ## What is the new behavior? All claims verified against the CLI source at current `develop` — including supabase/cli#6300, which upgraded the engine to `@supabase/pg-delta` 1.0.0-alpha.46 — against the pinned pg-delta package source (profile rules, format defaults, coverage doc), and against a live dogfood run of the documented workflows on `develop` `38f31b4` (two OSS corpus projects, warm shadow cache). - **`cli-workflows.mdx`**: adds a "Which diff engine you're on" note (`pg-delta` for new `supabase init` projects, `migra` for existing ones until they opt in by adding `[experimental.pgdelta] enabled = true`; per-run fallbacks `--use-migra` on `db diff` / `--diff-engine migra` on `db pull`); corrects `db pull` and `db diff` mechanics (shadow built from migrations vs. live database; the baseline history record is offered, not unconditional); switches the declarative flow to `supabase db schema declarative sync`; reworks the cleanup section around pg-delta output (uppercase keywords at max width 180, `format_options`, per-unit migration files with numeric segment suffixes, the `-- pg-delta: transaction=false` directive on genuinely non-transactional files, engine-neutral grant/revoke review guidance, coverage warnings + `--strict-coverage`); documents what pg-delta captures in managed schemas (user triggers, RLS policies on `auth` tables and on `storage.objects`/`storage.buckets`/`realtime.messages`) versus what it doesn't; adds key-command rows for the declarative commands and troubleshooting entries (`db pull` non-zero exit when in sync, the `schema_paths` warning, `PGDELTA_DEBUG=1` bundles under `supabase/.temp/pgdelta/v2/debug/`). - **`declarative-database-schemas.mdx`**: swaps `db diff -f` for `db schema declarative sync -f` throughout; replaces lexicographic/`schema_paths` ordering guidance with automatic dependency ordering and the `generate` export layout (`_cluster/`, reserved `_custom/`); bootstraps from production via `db schema declarative generate --linked` (explicit target + `--overwrite` in scripts) and refreshes via `db pull --declarative`; rewrites known caveats for pg-delta (DML including storage buckets, untracked object kinds + the `_custom/` escape hatch, managed schemas, extension-managed objects, and the two gates when adopting an existing schema tree: `[experimental.webhooks]` for `pg_net` migrations and declaring the tree's extensions) keeping the `migra` workflow and issue list under a legacy section for projects that haven't enabled it. - **`managing-environments.mdx`**: frames the verbose grant sample as legacy-engine output, notes that generated migrations can include grant statements on any engine, describes `--use-migra` as a single-run fallback, and adds a `db diff --strict-coverage` CI step. - **`backup-restore.mdx`**: replaces `db diff --linked --schema auth,storage` with a plain `db diff --linked` on `pg-delta` (keeping the `--schema auth,storage` form for the legacy engine) and explains what the engine includes (user triggers on managed tables, user RLS policies on `auth`, `storage.objects`/`storage.buckets`/`realtime.messages`) and what must be recreated manually. - **New `diff-engines.mdx` page** (from #49889): the single home for how the engine is selected, a behavior matrix for `pg-delta` versus `migra`, the per-command fallback flags, a procedure for switching an existing project (the first `db pull` after enabling may write a catch-up migration), and how to go back with `enabled = false`. Registered in navigation. A shared `diff_engine_check` partial replaces the inline engine parentheticals across seven pages, and a `managed_schemas_diff_capture` partial carries the managed-schema capture rules. - **CLI reference (`cli_v1_commands.yaml`, `cli_v1_config.yaml`)**: `db pull`, `db schema declarative sync`/`generate` flags and descriptions, `experimental.pgdelta.*` and `db.migrations.schema_paths` config keys, and the `db diff` description updated to describe both engines. Note that `cli_v1_commands.yaml` is generated from the CLI repo; [supabase/cli#6557](https://github.com/supabase/cli/pull/6557) carries the matching `db pull` example and overlay text so the next publish keeps it. - **`examples/prompts/declarative-database-schema.md`**: rewritten for the `db schema declarative sync` flow, with the `[experimental.pgdelta]` prerequisite. ## Additional context The first draft was written against pg-delta 1.0.0-alpha.42. supabase/cli#6300 (engine upgrade to alpha.46) then changed two documented behaviors, both reflected here: generated SQL now defaults to uppercase pretty-printed keywords, and user RLS policies on `storage.objects`/`storage.buckets`/`realtime.messages` are included via the engine's `SUPABASE_USER_POLICY_SURFACES` allowlist. A follow-up dogfood run on `develop` `38f31b4` then falsified three more claims (pg-delta emits no grant noise, `_schema_changes`/`_after_enum_values` multi-file names, directive on every split file), all corrected in the last commit. **Update (Sep 14 to 17):** [#49889](https://github.com/supabase/supabase/pull/49889) and [#50220](https://github.com/supabase/supabase/pull/50220) were merged into this branch, so this PR now carries the full stack. #50220 corrected the `schema_paths` warning wording (the CLI warns only when the setting lists paths), added `auth` RLS policies to the managed-schema partial, and described the migra initial pull accurately (the `pg_dump` skips managed schemas and the migra diff pass that follows appends the trigger and policy changes). It also reframed `pg-delta` as the default for every project ahead of supabase/cli#6391. That plan changed: no breaking default flip before Select, so [#50332](https://github.com/supabase/supabase/pull/50332) restores the opt-in framing (`pg-delta` requires `[experimental.pgdelta] enabled = true`, which `supabase init` writes for new projects) and also resolves the four CodeRabbit findings from the latest review round. Two claims are pending confirmation from the owning teams: that branching runs every migration in a transaction and ignores the `-- pg-delta: transaction=false` directive, and the `--db-url` pooler-versus-direct connection advice, which currently disagrees with the CLI's own `db pull` docs. Stale spots found in the CLI repo's own docs while verifying (out of scope here, worth follow-ups): four `SIDE_EFFECTS.md` files still claim lowercase output, `docs/supabase/db/diff.md` still lists `migra`-era "known failure cases" that alpha.46 fully models, the `supabase init` template's commented `format_options` example shows `maxWidth: 80` against an actual default of 180, and the CLI upgrade recipe appends `--experimental` even when the config already enables pg-delta. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01SUuaVmXLRbV6tZjzhka3cp <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Clarified `pg-delta` and legacy `migra` behavior, configuration, and switching guidance. * Expanded declarative schema workflows, including synchronization, migration generation, baselines, deployment, and legacy-engine support. * Documented managed schemas, permissions, extensions, transaction handling, dependency ordering, and troubleshooting. * Added guidance for strict coverage checks, output directories, non-interactive workflows, and declarative pull modes. * Added a dedicated diff engines guide and updated CLI navigation, backup and restore, branching, deployment, and CI documentation. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Wen Bo Xie <wenbox323@gmail.com>
399 lines
20 KiB
Plaintext
399 lines
20 KiB
Plaintext
---
|
||
id: 'declarative-database-schemas'
|
||
title: 'Declarative database schemas'
|
||
description: 'Manage your database schemas in one place and generate versioned migrations.'
|
||
subtitle: 'Manage your database schemas in one place and generate versioned migrations.'
|
||
---
|
||
|
||
## Overview
|
||
|
||
Declarative schemas provide a developer-friendly way to maintain [schema migrations](#schema-migrations).
|
||
|
||
[Migrations](/docs/guides/deployment/database-migrations) are traditionally managed imperatively (you provide the instructions on how exactly to change the database). This can lead to related information being scattered over multiple migration files. With declarative schemas, you instead declare the state you want your database to be in, and the instructions are generated for you.
|
||
|
||
Because the schema files are the source of truth, make every change by editing them, not through Studio or the SQL editor. Migrations are generated from your schema files with `supabase db schema declarative sync`, which compares the files against your migration history, not the live database. Changes made directly to the database are not picked up.
|
||
|
||
<Admonition type="note" title="Check your diff engine first">
|
||
|
||
<$Partial path="diff_engine_check.mdx" />
|
||
|
||
This guide describes the workflow on `pg-delta`. The `db schema declarative` commands require it and won't run on the legacy engine. For a single run of `sync` or `generate` without changing `config.toml`, pass the `--experimental` flag. `db pull --declarative` runs regardless of the `[experimental.pgdelta]` setting, but only use it on `pg-delta`. On the legacy engine it leaves `db diff` unable to run. If your project is still on `migra`, follow [Declarative schemas on the legacy `migra` engine](#declarative-schemas-on-the-legacy-migra-engine) instead.
|
||
|
||
</Admonition>
|
||
|
||
## Schema migrations
|
||
|
||
Schema migrations are SQL statements written in Data Definition Language. They are versioned in your `supabase/migrations` directory to ensure schema consistency between local and remote environments.
|
||
|
||
### Declaring your schema
|
||
|
||
<StepHikeCompact>
|
||
|
||
<StepHikeCompact.Step step={1}>
|
||
<StepHikeCompact.Details title="Create your first schema file">
|
||
Create a SQL file in `supabase/schemas` directory that defines an `employees` table.
|
||
</StepHikeCompact.Details>
|
||
|
||
<StepHikeCompact.Code>
|
||
|
||
```sql name=supabase/schemas/employees.sql
|
||
create table "employees" (
|
||
"id" integer not null,
|
||
"name" text
|
||
);
|
||
```
|
||
|
||
</StepHikeCompact.Code>
|
||
|
||
</StepHikeCompact.Step>
|
||
</StepHikeCompact>
|
||
|
||
<StepHikeCompact>
|
||
|
||
<StepHikeCompact.Step step={2}>
|
||
<StepHikeCompact.Details title="Generate a migration file">
|
||
Generate a migration file by diffing against your declared schema. This walkthrough passes `--no-apply` so the command only writes the file and the next step applies it. Without that flag, the command offers to apply the migration to your local database. Both `--apply` and the global `--yes` flag apply it without prompting, and a non-interactive run writes the file and silently skips the apply step.
|
||
</StepHikeCompact.Details>
|
||
|
||
<StepHikeCompact.Code>
|
||
|
||
```bash name=Terminal
|
||
supabase db schema declarative sync -f create_employees_table --no-apply
|
||
```
|
||
|
||
</StepHikeCompact.Code>
|
||
|
||
</StepHikeCompact.Step>
|
||
</StepHikeCompact>
|
||
|
||
<StepHikeCompact>
|
||
|
||
<StepHikeCompact.Step step={3}>
|
||
<StepHikeCompact.Details title="Start the local database and apply migrations">
|
||
Start the local database first. Then, apply the migration manually to see your schema changes in the local Dashboard.
|
||
</StepHikeCompact.Details>
|
||
|
||
<StepHikeCompact.Code>
|
||
|
||
```bash name=Terminal
|
||
supabase start
|
||
supabase migration up
|
||
```
|
||
|
||
</StepHikeCompact.Code>
|
||
|
||
</StepHikeCompact.Step>
|
||
</StepHikeCompact>
|
||
|
||
### Updating your schema
|
||
|
||
<Admonition type="caution" label="Edit the schema files, not the database">
|
||
|
||
With declarative schemas, the files in `supabase/schemas/` are the source of truth. `supabase db schema declarative sync` compares those files against your migrations. It does not read the live database. Changes you make directly (Studio, the SQL editor, `psql`) are invisible to the diff, which reports "No schema changes found" and silently drops the change. Always edit the schema files, then run `sync`.
|
||
|
||
Don't use `supabase db diff` here, because it diffs a live database against your migrations and never uses `supabase/schemas/` as its baseline.
|
||
|
||
</Admonition>
|
||
|
||
<StepHikeCompact>
|
||
|
||
<StepHikeCompact.Step step={1}>
|
||
<StepHikeCompact.Details title="Add a new column">
|
||
Edit `supabase/schemas/employees.sql` file to add a new column to `employees` table.
|
||
</StepHikeCompact.Details>
|
||
|
||
<StepHikeCompact.Code>
|
||
|
||
```sql name=supabase/schemas/employees.sql
|
||
create table "employees" (
|
||
"id" integer not null,
|
||
"name" text,
|
||
"age" smallint not null
|
||
);
|
||
```
|
||
|
||
</StepHikeCompact.Code>
|
||
|
||
</StepHikeCompact.Step>
|
||
</StepHikeCompact>
|
||
|
||
<Admonition type="note">
|
||
|
||
Some entities like views and enums expect columns to be declared in a specific order. To avoid messy diffs, always append new columns to the end of the table.
|
||
|
||
</Admonition>
|
||
|
||
<StepHikeCompact>
|
||
|
||
<StepHikeCompact.Step step={2}>
|
||
<StepHikeCompact.Details title="Generate a new migration">
|
||
Diff existing migrations against your declared schema.
|
||
</StepHikeCompact.Details>
|
||
|
||
<StepHikeCompact.Code>
|
||
|
||
```bash name=Terminal
|
||
supabase db schema declarative sync -f add_age --no-apply
|
||
```
|
||
|
||
</StepHikeCompact.Code>
|
||
|
||
</StepHikeCompact.Step>
|
||
</StepHikeCompact>
|
||
|
||
<StepHikeCompact>
|
||
|
||
<StepHikeCompact.Step step={3}>
|
||
<StepHikeCompact.Details title="Review the generated migration">
|
||
Verify that the generated migration contain a single incremental change.
|
||
</StepHikeCompact.Details>
|
||
|
||
<StepHikeCompact.Code>
|
||
|
||
```sql name=supabase/migrations/<timestamp>_add_age.sql
|
||
alter table "public"."employees" add column "age" smallint not null;
|
||
```
|
||
|
||
</StepHikeCompact.Code>
|
||
|
||
</StepHikeCompact.Step>
|
||
</StepHikeCompact>
|
||
|
||
<StepHikeCompact>
|
||
<StepHikeCompact.Step step={4}>
|
||
<StepHikeCompact.Details title="Apply the pending migration">
|
||
Start the database locally and apply the pending migration.
|
||
</StepHikeCompact.Details>
|
||
|
||
<StepHikeCompact.Code>
|
||
|
||
```bash name=Terminal
|
||
supabase migration up
|
||
```
|
||
|
||
</StepHikeCompact.Code>
|
||
|
||
</StepHikeCompact.Step>
|
||
</StepHikeCompact>
|
||
|
||
### Deploying your schema changes
|
||
|
||
<StepHikeCompact>
|
||
|
||
<StepHikeCompact.Step step={1}>
|
||
<StepHikeCompact.Details title="Sign in to the Supabase CLI">
|
||
[Sign in](/docs/reference/cli/supabase-login) via the Supabase CLI.
|
||
</StepHikeCompact.Details>
|
||
|
||
<StepHikeCompact.Code>
|
||
|
||
```bash name=Terminal
|
||
supabase login
|
||
```
|
||
|
||
</StepHikeCompact.Code>
|
||
|
||
</StepHikeCompact.Step>
|
||
|
||
<StepHikeCompact.Step step={2}>
|
||
<StepHikeCompact.Details title="Link your remote project">
|
||
Follow the on-screen prompts to [link](/docs/reference/cli/supabase-link) your remote project.
|
||
</StepHikeCompact.Details>
|
||
|
||
<StepHikeCompact.Code>
|
||
|
||
```bash name=Terminal
|
||
supabase link
|
||
```
|
||
|
||
</StepHikeCompact.Code>
|
||
|
||
</StepHikeCompact.Step>
|
||
|
||
<StepHikeCompact.Step step={3}>
|
||
<StepHikeCompact.Details title="Deploy database changes">
|
||
[Push](/docs/reference/cli/supabase-db-push) your changes to the remote database.
|
||
</StepHikeCompact.Details>
|
||
|
||
<StepHikeCompact.Code>
|
||
|
||
```bash name=Terminal
|
||
supabase db push
|
||
```
|
||
|
||
</StepHikeCompact.Code>
|
||
|
||
</StepHikeCompact.Step>
|
||
</StepHikeCompact>
|
||
|
||
### Managing dependencies
|
||
|
||
As your database schema evolves, you will probably start using more advanced entities like views and functions. These entities are notoriously verbose to manage using plain migrations because the entire body must be recreated whenever there is a change. Using declarative schema, you can now edit them in-place so it’s much easier to review.
|
||
|
||
```sql name=supabase/schemas/employees.sql
|
||
create table "employees" (
|
||
"id" integer not null,
|
||
"name" text,
|
||
"age" smallint not null
|
||
);
|
||
|
||
create view "profiles" as
|
||
select id, name from "employees";
|
||
|
||
create function "get_age"(employee_id integer) RETURNS smallint
|
||
LANGUAGE "sql"
|
||
AS $$
|
||
select age
|
||
from employees
|
||
where id = employee_id;
|
||
$$;
|
||
```
|
||
|
||
You don't need to order your schema files manually. When generating a migration, the engine analyzes the dependencies between your statements, such as foreign keys, views over tables, and functions used by triggers, and orders them automatically. File names and directory layout are for readability only. If your statements contain a dependency cycle, the command fails with a diagnostic instead of producing a broken migration.
|
||
|
||
This means you can organize `supabase/schemas/` however you like. For example, one file per table:
|
||
|
||
```bash
|
||
.
|
||
└── supabase/
|
||
├── schemas/
|
||
│ ├── employees.sql
|
||
│ └── managers.sql
|
||
└── migrations/
|
||
├── 20241004112233_create_employees_table.sql
|
||
├── 20241005112233_add_employee_age.sql
|
||
└── 20241006112233_add_managers_table.sql
|
||
```
|
||
|
||
Or the per-schema layout that `supabase db schema declarative generate` produces, with one directory per database schema (such as `supabase/schemas/public/tables/employees.sql`) and cluster-level objects like roles under a reserved `_cluster/` directory. You can also create a reserved `_custom/` directory for hand-authored SQL covering objects the engine doesn't track. `generate` doesn't create it, and once it exists the export never writes to it or prunes it. See [Known caveats](#known-caveats).
|
||
|
||
<Admonition type="note">
|
||
|
||
Under the `pg-delta` engine, the `[db.migrations].schema_paths` setting from earlier CLI versions no longer controls declarative file ordering, because ordering is automatic. The CLI warns when the setting lists any paths. On the legacy `migra` engine, `schema_paths` still controls the order in which declarative files are applied. See [Ordering schema files](#ordering-schema-files-on-the-legacy-engine).
|
||
|
||
</Admonition>
|
||
|
||
### Pulling in your production schema
|
||
|
||
To set up declarative schemas on an existing project, export your production schema into declarative files:
|
||
|
||
```bash name=Terminal
|
||
supabase db schema declarative generate --linked
|
||
```
|
||
|
||
This writes per-object SQL files under `supabase/schemas/` (or the directory set by `experimental.pgdelta.declarative_schema_path`), organized by database schema. In scripts, pass the target explicitly (`--local`, `--linked`, or `--db-url`) and add `--overwrite` to replace an existing tree without prompting. To later refresh the declarative tree from the remote database (for example, after a change was deployed outside your local workflow), run `supabase db pull --declarative`. It replaces the schema files without creating a migration or touching migration history.
|
||
|
||
`generate` writes schema files only. Because `sync` diffs your schema files against your migration history, that history must describe the same database before your first `sync`. If your project has no migrations yet, run `supabase db pull` first to create a baseline migration. Without a baseline, the first `sync` regenerates the entire schema as one migration. That migration can apply cleanly to an empty local database and still fail on `db push`, because the remote database already has those objects.
|
||
|
||
### Rolling back a schema change
|
||
|
||
During development, you may want to rollback a migration to keep your new schema changes in a single migration file. This can be done by resetting your local database to a previous version.
|
||
|
||
```bash name=Terminal
|
||
supabase db reset --version 20241005112233
|
||
```
|
||
|
||
After a reset, you can [edit the schema](#updating-your-schema) and regenerate a new migration file. Note that you should not reset a version that's already deployed to production.
|
||
|
||
If you need to rollback a migration that's already deployed, you should first revert changes to the schema files. Then you can generate a new migration file containing the down migration. This ensures your production migrations are always rolling forward.
|
||
|
||
<Admonition type="danger">
|
||
|
||
SQL statements generated in a down migration are usually destructive. You must review them carefully to avoid unintentional data loss.
|
||
|
||
</Admonition>
|
||
|
||
## Known caveats
|
||
|
||
Schema diffs are generated by `pg-delta`, which models most database entities, including tables, views, materialized views, functions, triggers, RLS policies, grants, comments, domains, partitions, and publications. There are still cases it cannot capture. Review every generated migration before committing.
|
||
|
||
### Data manipulation language
|
||
|
||
DML statements such as `insert`, `update`, and `delete` are never captured by a schema diff. This includes storage buckets, which are rows in the `storage.buckets` table rather than schema objects. A DML statement inside a declarative schema file is an error. Keep data changes in [seed files](/docs/guides/local-development/seeding-your-database) or hand-written [versioned migrations](/docs/guides/deployment/database-migrations).
|
||
|
||
### Default privileges on new objects
|
||
|
||
Both engines treat permissions as part of the schema state. When you create a new object, generated migrations can include `GRANT`/`REVOKE` statements you didn't write, reflecting default privileges. If you haven't customized permissions, these lines are safe to remove.
|
||
|
||
### Object kinds that aren't tracked
|
||
|
||
Some object kinds are not tracked by the engine: casts, operators, operator classes and families, text search configurations, dictionaries, parsers, and templates, statistics objects, languages, transforms, and parameter ACLs. They are never silently dropped. The engine reports them as warnings, and you can pass `--strict-coverage` to turn those warnings into hard failures (useful in CI).
|
||
|
||
To use these objects with declarative schemas, create the reserved `supabase/schemas/_custom/` directory if it doesn't exist and put their SQL there so dependent objects still resolve. `generate` never creates, overwrites, or prunes that directory. Deliver the change itself through a versioned migration, and make sure that migration sorts before the generated migration that depends on the object. Otherwise `db reset` and every later `sync` fail. Parameter ACLs are the exception because they live in a catalog shared by every database in the cluster. Keep them out of `_custom/` and manage them through versioned migrations only.
|
||
|
||
### Supabase-managed schemas
|
||
|
||
<$Partial path="managed_schemas_diff_capture.mdx" />
|
||
|
||
Manage those objects through versioned migrations.
|
||
|
||
### Extension-managed objects
|
||
|
||
Objects that belong to an extension are recognized as the extension's, not yours. Anything an extension owns is excluded from diffs. Objects extensions create as they run, such as partitions maintained by `pg_partman` and queue tables created by `pgmq`, are never emitted as raw `create table` or `drop table` statements. The diff expresses changes to them through the extension's own API instead, such as `select pgmq.drop_queue('q');` or a `delete from partman.part_config` row, and the CLI flags those statements as destructive. Create and change these objects through the extension's own functions, and review any generated API calls before committing.
|
||
|
||
### Adopting an existing schema tree
|
||
|
||
Two checks gate `sync` on a schema tree the CLI didn't generate:
|
||
|
||
- If your migrations call `pg_net` (for example, database webhooks), add `[experimental.webhooks]` with `enabled = true` to `config.toml` first. The section isn't part of the default `init` template.
|
||
- Declare the extensions your schema files depend on. The check trips for `pg_net` and for extensions your own migrations create. When it does, interactive `sync` offers to add the missing declaration and re-plan, or to stage a fresh export into a sibling `-next` directory and print the commands to adopt it. A non-interactive run only prints those commands. Extensions the local stack already ships, such as `pgcrypto` and `uuid-ossp`, don't trip the check even when undeclared, so declare them yourself.
|
||
|
||
## Declarative schemas on the legacy `migra` engine [#declarative-schemas-on-the-legacy-migra-engine]
|
||
|
||
Projects that haven't enabled `pg-delta` can still use declarative schemas. The workflow has the same shape, but the commands differ, and the `db schema declarative` commands aren't available. See [Diff engines](/docs/guides/local-development/diff-engines) for how to check which engine you're on and how the engines compare.
|
||
|
||
<Admonition type="caution">
|
||
|
||
On the legacy engine, don't run `supabase db pull --declarative`. The command runs regardless of the `[experimental.pgdelta]` setting, but the tree it writes is only readable by the `db schema declarative` commands. `db diff` on the legacy engine tries to load that tree as its baseline, in lexicographic order, and fails on the first file. If you've already run it, delete the generated tree under `supabase/schemas/` or [switch to `pg-delta`](#switching-to-pg-delta).
|
||
|
||
</Admonition>
|
||
|
||
### Generating migrations on the legacy engine
|
||
|
||
On the legacy engine, `supabase db diff` reads your declarative files. When `supabase/schemas/` contains files, it compares them against your migrations instead of reading the live database. Generate migrations with the local stack stopped.
|
||
|
||
1. Edit the files in `supabase/schemas/`.
|
||
2. Stop the local stack and generate a migration:
|
||
|
||
```bash
|
||
supabase stop
|
||
supabase db diff -f add_age
|
||
```
|
||
|
||
3. Start the stack and apply the migration:
|
||
|
||
```bash
|
||
supabase start
|
||
supabase migration up
|
||
```
|
||
|
||
As on `pg-delta`, changes made directly through Studio, the SQL editor, or `psql` are not picked up. Always edit the schema files, then diff.
|
||
|
||
### Ordering schema files on the legacy engine
|
||
|
||
The legacy engine applies your schema files in lexicographic order. The order matters when tables reference each other, because the parent table must be created first. To control the order, list files or glob patterns under `[db.migrations].schema_paths` in `config.toml`. The CLI processes the entries in the order you list them: it expands each pattern, sorts that pattern's matches lexicographically, and skips any file an earlier entry already matched. This example always runs `employees.sql` first:
|
||
|
||
```toml name=supabase/config.toml
|
||
[db.migrations]
|
||
schema_paths = [
|
||
"./schemas/employees.sql",
|
||
"./schemas/*.sql",
|
||
]
|
||
```
|
||
|
||
### Starting from an existing database on the legacy engine
|
||
|
||
`supabase db schema declarative generate` isn't available on the legacy engine. Instead, dump your production schema into a single file and split it into smaller files over time:
|
||
|
||
```bash
|
||
supabase db dump > supabase/schemas/prod.sql
|
||
```
|
||
|
||
### Limitations of the legacy engine [#limitations-of-the-legacy-engine]
|
||
|
||
In addition to the caveats above, the legacy [`migra`](https://github.com/djrobstep/migra) engine has these limitations: [view owner and grants](https://github.com/djrobstep/migra/issues/160#issuecomment-1702983833), [security invoker on views](https://github.com/djrobstep/migra/issues/234) (the setting is silently dropped from the view definition), [indexes on a materialized view aren't restored when the view is recreated](https://github.com/djrobstep/migra/issues/194), [alter policy statements](https://github.com/djrobstep/schemainspect/blob/master/schemainspect/pg/obj.py#L228), [column privileges](https://github.com/djrobstep/schemainspect/pull/67), [comments](https://github.com/djrobstep/migra/issues/69), [roles](https://github.com/djrobstep/migra/issues/240), [`alter publication ... add table`](https://github.com/supabase/cli/issues/883), and [`create domain` statements](https://github.com/supabase/cli/issues/2137). Add those entities through versioned migrations instead.
|
||
|
||
### Switching to `pg-delta` [#switching-to-pg-delta]
|
||
|
||
Follow [Switch an existing project to `pg-delta`](/docs/guides/local-development/diff-engines#switch-an-existing-project-to-pg-delta). For a declarative project, the steps that change your day-to-day work are removing `schema_paths`, replacing `db diff` with `db schema declarative sync` in scripts and CI, and dropping the `supabase stop` step.
|