mirror of
https://github.com/supabase/supabase.git
synced 2026-10-09 19:35:06 +03:00
477 lines
16 KiB
Plaintext
477 lines
16 KiB
Plaintext
---
|
|
id: 'cli-workflows'
|
|
title: 'CLI workflows'
|
|
description: 'End-to-end workflows for local development with the Supabase CLI.'
|
|
subtitle: 'End-to-end workflows for local development with the Supabase CLI.'
|
|
---
|
|
|
|
This guide walks through two common starting points for local development with the Supabase CLI, and shows how they converge into the same daily workflow. By the end, you'll have a `./supabase` directory in your repo that anyone can clone to recreate the full project — locally or on a fresh remote instance.
|
|
|
|
**Scenario A**: You have an existing project on the Supabase platform and want to move to a proper local development workflow.
|
|
|
|
**Scenario B**: You're starting from scratch locally and will eventually push to a remote instance.
|
|
|
|
Both lead to the same place: database schema and migrations tracked in version control, with seed data for local development.
|
|
|
|
## What the local development stack is
|
|
|
|
Running `supabase start` launches the full Supabase stack on your machine via Docker: Postgres, Auth (GoTrue), Storage, Realtime, Edge Functions, PostgREST, Studio, and more.
|
|
|
|
This local stack is **for development only**. It is not hardened for production use and must never be exposed to external traffic. It has no TLS, no rate limiting, and default credentials. Use it to develop and test — deploy to the [Supabase Platform](https://supabase.com) or a proper self-hosted setup for anything beyond that.
|
|
|
|
All local configuration lives in `./supabase/config.toml`, created by `supabase init`.
|
|
|
|
## Installing the CLI
|
|
|
|
<Tabs
|
|
scrollable
|
|
size="small"
|
|
type="underlined"
|
|
defaultActiveId="homebrew"
|
|
queryGroup="install-method"
|
|
>
|
|
<TabPanel id="homebrew" label="Homebrew (macOS/Linux)">
|
|
|
|
```bash
|
|
brew install supabase/tap/supabase
|
|
```
|
|
|
|
The command is `supabase`. Updates: `brew upgrade supabase`.
|
|
|
|
</TabPanel>
|
|
<TabPanel id="npm" label="npm">
|
|
|
|
```bash
|
|
npm install supabase --save-dev
|
|
```
|
|
|
|
The command is `npx supabase`. Unlike many npm packages, `npx supabase` does **not** auto-download the package on the fly — you must install it first. Pin the version in `package.json` so your entire team uses the same CLI version. Updates: `npm update supabase`.
|
|
|
|
</TabPanel>
|
|
</Tabs>
|
|
|
|
<Admonition type="note" label="Command syntax throughout this guide">
|
|
|
|
This guide uses `supabase` in all examples. If you installed via npm, substitute `npx supabase` everywhere.
|
|
|
|
</Admonition>
|
|
|
|
**Prerequisites**: [Docker Desktop](https://www.docker.com/products/docker-desktop/), [OrbStack](https://orbstack.dev/), [Colima](https://github.com/abiosoft/colima), or another Docker-compatible container runtime. At least 7 GB of RAM allocated to Docker is recommended.
|
|
|
|
## The `./supabase` directory
|
|
|
|
After `supabase init`, your project contains a `./supabase` directory. Here's what goes in it and what to commit:
|
|
|
|
| Path | Purpose | Commit? |
|
|
|---|---|---|
|
|
| `config.toml` | Local stack configuration (ports, auth settings, etc.) | Yes |
|
|
| `migrations/` | Timestamped SQL migration files, applied in order | Yes |
|
|
| `seed.sql` | Dev/test data, applied after migrations on `start` and `db reset` | Yes |
|
|
| `schemas/` | Declarative schema files (if using that approach) | Yes |
|
|
| `.temp/`, `.branches/` | CLI internal state | No (gitignored) |
|
|
|
|
The `config.toml` is safe to commit — it contains no secrets by default. If you add sensitive values (OAuth credentials, API keys), use the `env()` function to reference environment variables instead of hardcoding them. See [Managing config and secrets](/docs/guides/local-development/managing-config).
|
|
|
|
## Scenario A: Existing platform project to local development
|
|
|
|
You've built a project on the Supabase platform — tables created via the Dashboard, SQL editor, or client libraries. Now you want a local dev setup with everything in version control.
|
|
|
|
### Step 1: Initialize
|
|
|
|
In your project root:
|
|
|
|
```bash
|
|
supabase init
|
|
```
|
|
|
|
This creates `./supabase/config.toml`. If you already have a project directory with application code, run this at the root — the `supabase/` directory will sit alongside your app code.
|
|
|
|
### Step 2: Authenticate
|
|
|
|
```bash
|
|
supabase login
|
|
```
|
|
|
|
Opens a browser to generate an access token. The token is stored locally and used for all subsequent CLI commands that interact with the platform.
|
|
|
|
### Step 3: Link to your remote project
|
|
|
|
```bash
|
|
supabase link --project-ref <project-id>
|
|
```
|
|
|
|
Find your project ID in the Supabase Dashboard URL: `https://supabase.com/dashboard/project/<project-id>`.
|
|
|
|
This tells the CLI which remote project to connect to for `db pull`, `db push`, and other remote operations. You'll be prompted for the database password — this is the password set when you created the project.
|
|
|
|
### Step 4: Pull the remote schema
|
|
|
|
```bash
|
|
supabase db pull
|
|
```
|
|
|
|
This connects to your remote database, dumps the entire schema, and saves it as a migration file:
|
|
|
|
```
|
|
supabase/migrations/<timestamp>_remote_schema.sql
|
|
```
|
|
|
|
This initial migration is your baseline — it represents the current state of your database. All future changes build on top of it.
|
|
|
|
<Admonition type="tip">
|
|
|
|
If you also use Supabase Auth or Storage and have customized their schemas, pull them separately:
|
|
|
|
```bash
|
|
supabase db pull --schema auth -f pull-auth-schema
|
|
supabase db pull --schema storage -f pull-storage-schema
|
|
```
|
|
|
|
These schemas are managed by Supabase and typically don't need to be pulled unless you've made custom modifications.
|
|
|
|
</Admonition>
|
|
|
|
### Step 5: Create seed data
|
|
|
|
You have two options:
|
|
|
|
**Option A: Dump existing data from remote** (then clean it up):
|
|
|
|
```bash
|
|
supabase db dump --data-only --linked > supabase/seed.sql
|
|
```
|
|
|
|
<Admonition type="caution">
|
|
|
|
Review and clean up the dump before committing. Remove production user data, secrets, personal information, and anything sensitive. Keep only representative test data that a developer needs to work with the project.
|
|
|
|
</Admonition>
|
|
|
|
**Option B: Write seed data by hand** (recommended for most projects):
|
|
|
|
Create `supabase/seed.sql` with INSERT statements that set up a useful local development state — a few test users, sample data, etc. This is often better than dumping production data because you control exactly what's in it.
|
|
|
|
### Step 6: Verify
|
|
|
|
```bash
|
|
supabase start
|
|
supabase db reset
|
|
```
|
|
|
|
`db reset` destroys the local database and recreates it from scratch: applies all migrations in order, then runs `seed.sql`. If this succeeds, your setup is reproducible — anyone who clones the repo can do the same.
|
|
|
|
### Step 7: Commit
|
|
|
|
```bash
|
|
git add supabase/
|
|
git commit -m "add supabase local development setup"
|
|
```
|
|
|
|
Your project now has a fully reproducible local development environment.
|
|
|
|
<Admonition type="note" label="What about declarative schemas?">
|
|
|
|
For an existing project, the pulled migration already serves as your schema baseline. You don't need to also create a `schemas/` directory — that would mean maintaining two representations of the same schema. If you want to adopt declarative schemas later, see [Declarative database schemas](/docs/guides/local-development/declarative-database-schemas). For day-to-day changes going forward, see [The daily workflow](#the-daily-workflow) below.
|
|
|
|
</Admonition>
|
|
|
|
## Scenario B: Starting fresh locally
|
|
|
|
No remote project yet. You're building from scratch and want to do it right from the start.
|
|
|
|
### Step 1: Initialize
|
|
|
|
```bash
|
|
supabase init
|
|
```
|
|
|
|
### Step 2: Start the local stack
|
|
|
|
```bash
|
|
supabase start
|
|
```
|
|
|
|
On first run, Docker images are pulled — this takes a few minutes. Subsequent starts are fast.
|
|
|
|
Once running, the CLI outputs local service URLs and credentials:
|
|
|
|
```
|
|
API URL: http://127.0.0.1:54321
|
|
GraphQL URL: http://127.0.0.1:54321/graphql/v1
|
|
S3 Storage URL: http://127.0.0.1:54321/storage/v1/s3
|
|
DB URL: postgresql://postgres:postgres@127.0.0.1:54322/postgres
|
|
Studio URL: http://127.0.0.1:54323
|
|
...
|
|
anon key: eyJ...
|
|
service_role key: eyJ...
|
|
```
|
|
|
|
Use the Studio URL to access a local instance of the Supabase Dashboard.
|
|
|
|
### Step 3: Create your schema
|
|
|
|
Two approaches — pick one:
|
|
|
|
**Option A: Declarative schema** (recommended for new projects)
|
|
|
|
Create a schema file that declares the state you want your database to be in:
|
|
|
|
```sql title="supabase/schemas/schema.sql"
|
|
create table public.todos (
|
|
id bigint generated by default as identity primary key,
|
|
created_at timestamptz default now() not null,
|
|
title text not null,
|
|
is_complete boolean default false not null,
|
|
user_id uuid references auth.users (id) default auth.uid() not null
|
|
);
|
|
|
|
alter table public.todos enable row level security;
|
|
|
|
create policy "Users can read their own todos"
|
|
on public.todos for select
|
|
using (auth.uid() = user_id);
|
|
|
|
create policy "Users can create their own todos"
|
|
on public.todos for insert
|
|
with check (auth.uid() = user_id);
|
|
```
|
|
|
|
Then generate a migration from it:
|
|
|
|
```bash
|
|
supabase db diff -f initial-schema
|
|
```
|
|
|
|
This compares your declared schema against the current (empty) database and generates a migration file in `supabase/migrations/`.
|
|
|
|
**Option B: Write the migration directly**
|
|
|
|
```bash
|
|
supabase migration new initial-schema
|
|
```
|
|
|
|
This creates an empty file at `supabase/migrations/<timestamp>_initial-schema.sql`. Write your SQL in it, then apply:
|
|
|
|
```bash
|
|
supabase db reset
|
|
```
|
|
|
|
### Step 4: Add seed data
|
|
|
|
Create `supabase/seed.sql`:
|
|
|
|
```sql title="supabase/seed.sql"
|
|
-- Create a test user (Supabase Auth)
|
|
-- Note: in local dev, email confirmation is disabled by default
|
|
insert into auth.users (id, email, raw_user_meta_data)
|
|
values ('d0e3c8f0-1234-5678-9abc-def012345678', 'test@example.com', '{}');
|
|
|
|
-- Seed application data
|
|
insert into public.todos (title, user_id)
|
|
values
|
|
('Buy groceries', 'd0e3c8f0-1234-5678-9abc-def012345678'),
|
|
('Write documentation', 'd0e3c8f0-1234-5678-9abc-def012345678');
|
|
```
|
|
|
|
### Step 5: Verify
|
|
|
|
```bash
|
|
supabase db reset
|
|
```
|
|
|
|
Drops everything, applies migrations, runs seed. If this passes, your project is reproducible.
|
|
|
|
### Step 6: Commit
|
|
|
|
```bash
|
|
git add supabase/
|
|
git commit -m "add supabase local development setup"
|
|
```
|
|
|
|
## The daily workflow
|
|
|
|
Both scenarios converge here. You have a working `./supabase` directory in your repo. Here's how day-to-day development works.
|
|
|
|
### Making schema changes
|
|
|
|
<Tabs
|
|
scrollable
|
|
size="small"
|
|
type="underlined"
|
|
defaultActiveId="declarative"
|
|
queryGroup="schema-approach"
|
|
>
|
|
<TabPanel id="declarative" label="Declarative schemas">
|
|
|
|
1. Edit your schema file(s) in `supabase/schemas/` (add a table, a column, a policy, etc.)
|
|
2. Generate a migration:
|
|
|
|
```bash
|
|
supabase db diff -f add-due-date-to-todos
|
|
```
|
|
|
|
3. Review the generated migration file — see [Cleaning up generated migrations](#cleaning-up-generated-migrations)
|
|
4. Verify the full chain:
|
|
|
|
```bash
|
|
supabase db reset
|
|
```
|
|
|
|
5. Commit the schema file **and** the migration together
|
|
|
|
</TabPanel>
|
|
<TabPanel id="imperative" label="Imperative migrations">
|
|
|
|
**If you made changes through the local Studio UI:**
|
|
|
|
```bash
|
|
supabase db diff -f add-due-date-to-todos
|
|
```
|
|
|
|
This captures your UI changes as a migration file.
|
|
|
|
**If you prefer to write SQL directly:**
|
|
|
|
```bash
|
|
supabase migration new add-due-date-to-todos
|
|
```
|
|
|
|
Write the SQL in the generated file. Then verify:
|
|
|
|
```bash
|
|
supabase db reset
|
|
```
|
|
|
|
Commit the migration.
|
|
|
|
</TabPanel>
|
|
</Tabs>
|
|
|
|
### Staying in sync with your team
|
|
|
|
When someone else pushes new migrations:
|
|
|
|
```bash
|
|
git pull
|
|
supabase db reset
|
|
```
|
|
|
|
`db reset` replays all migrations from scratch, so you'll always match the current state of the repo.
|
|
|
|
## Pushing to a remote project
|
|
|
|
When you're ready to deploy your schema to a remote Supabase instance:
|
|
|
|
```bash
|
|
# Authenticate (if not already)
|
|
supabase login
|
|
|
|
# Link to the remote project (if not already)
|
|
supabase link --project-ref <project-id>
|
|
|
|
# Preview what will be applied
|
|
supabase db push --dry-run
|
|
|
|
# Apply migrations
|
|
supabase db push
|
|
```
|
|
|
|
`db push` applies only migrations that haven't been applied to the remote yet. It tracks this via the `supabase_migrations.schema_migrations` table created automatically on the remote database.
|
|
|
|
To also seed a fresh remote instance (dev/staging environments only):
|
|
|
|
```bash
|
|
supabase db push --include-seed
|
|
```
|
|
|
|
<Admonition type="caution">
|
|
|
|
Never use `--include-seed` on a production database. Seed data is for development and testing.
|
|
|
|
</Admonition>
|
|
|
|
For multi-environment setups with CI/CD (feature branches → staging → production), see [Managing Environments](/docs/guides/deployment/managing-environments).
|
|
|
|
## Key commands at a glance
|
|
|
|
| Command | What it does |
|
|
|---|---|
|
|
| `supabase init` | Creates `./supabase/config.toml` |
|
|
| `supabase start` | Starts the local stack, applies migrations + seed |
|
|
| `supabase stop` | Stops the local stack (data persists until `db reset`) |
|
|
| `supabase db reset` | Destroys local DB, reapplies all migrations + seed from scratch |
|
|
| `supabase db diff -f <name>` | Generates a migration by diffing current DB state against a shadow database |
|
|
| `supabase db pull` | Pulls remote schema into a new local migration file |
|
|
| `supabase db push` | Applies unapplied local migrations to the remote database |
|
|
| `supabase db dump` | Exports remote DB schema (or `--data-only` for data) via `pg_dump` |
|
|
| `supabase migration new <name>` | Creates an empty migration file |
|
|
| `supabase migration list` | Compares local migrations against remote migration history |
|
|
| `supabase link --project-ref` | Connects local project to a remote Supabase project |
|
|
| `supabase login` | Authenticates with the Supabase platform |
|
|
|
|
## Cleaning up generated migrations
|
|
|
|
When `supabase db diff` generates a migration, it may include statements that are technically correct but noisy. Review every generated migration before committing.
|
|
|
|
### Grants
|
|
|
|
You may see lines like:
|
|
|
|
```sql
|
|
GRANT ALL ON TABLE public.todos TO anon;
|
|
GRANT ALL ON TABLE public.todos TO authenticated;
|
|
GRANT ALL ON TABLE public.todos TO service_role;
|
|
```
|
|
|
|
These appear because the diff tool treats permissions as part of the schema state. For tables in the `public` schema, these grants are applied by default and the lines are redundant. They're harmless — but if you want clean migrations, you can remove them. Be consistent across your team about whether you keep or remove them.
|
|
|
|
### Revoke/re-grant patterns
|
|
|
|
Sometimes a diff produces:
|
|
|
|
```sql
|
|
REVOKE ALL ON TABLE public.todos FROM anon;
|
|
GRANT ALL ON TABLE public.todos TO anon;
|
|
```
|
|
|
|
This is the diff tool being overly cautious. If you haven't changed permissions, these lines can be safely removed.
|
|
|
|
### Extension statements
|
|
|
|
`CREATE EXTENSION IF NOT EXISTS ...` may appear. Keep these if the extension is required by your migration. Remove them if the extension is already created by a previous migration or is part of the default Supabase setup.
|
|
|
|
### Known limitations of `db diff`
|
|
|
|
The diff is generated by the [migra](https://github.com/djrobstep/migra) tool. It has limitations:
|
|
|
|
- **DML is not captured**: INSERT, UPDATE, DELETE statements are not tracked. If your change involves data, add those statements to the migration manually.
|
|
- **RLS policy renames** show up as a drop + create (not a rename). The result is functionally identical but worth knowing.
|
|
- **Some view properties** (like `security_invoker`) may not diff correctly.
|
|
|
|
When in doubt, review the generated SQL and adjust it manually. Treat `db diff` output as a draft, not a final migration.
|
|
|
|
## Troubleshooting
|
|
|
|
**`db reset` fails with a migration error**
|
|
|
|
The output will show which migration file failed and the SQL error. Fix the migration file, then run `db reset` again.
|
|
|
|
**`db push` says migrations are already applied**
|
|
|
|
The remote database already has those migrations in its history. Run `supabase migration list` to compare local vs. remote state. If they're out of sync, use `supabase migration repair` to correct the remote history.
|
|
|
|
**Schema drift: remote was changed outside of migrations**
|
|
|
|
If someone modified the remote database directly (via Dashboard, SQL editor, etc.), run `supabase db pull` to capture those changes as a new migration file. Then `supabase db reset` locally to verify everything still works.
|
|
|
|
**Docker issues on `supabase start`**
|
|
|
|
Ensure Docker is running and has at least 7 GB of RAM allocated. If containers fail health checks, try:
|
|
|
|
```bash
|
|
supabase stop
|
|
supabase start
|
|
```
|
|
|
|
If problems persist, `supabase stop --no-backup` for a clean restart (this removes local database data).
|