Skip to content
Local Development

本地开发工作流程

Set up and run your day-to-day local development workflow with the Supabase CLI.

本指南介绍了使用 Supabase CLI 本地开发的两种常见起点,并展示了它们如何汇合到相同的日常工作流程中。到最后,你的仓库中将会有一个 ./supabase 目录,任何人都可以克隆它来在本地或全新的远程实例上重现完整项目。

🌐 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.

有两个起点,但都通向同一个地方:数据库模式和在版本控制中跟踪的迁移,以及用于本地开发的种子数据。

🌐 There are two starting points, both leading to the same place: database schema and migrations tracked in version control, with seed data for local development.

在你开始之前 #

🌐 Before you begin

你需要安装 Supabase CLI,并运行一个支持 Docker 的运行环境。如果你还没设置好,可以查看 安装和运行 CLI 来获取 macOS、Windows 和 Linux 的安装指南,以及 supabase start 启动了什么服务和如何访问每个服务的详细信息。

🌐 You need the Supabase CLI installed and a Docker-compatible runtime running. If you haven't set these up yet, see Install and run the CLI for installation across macOS, Windows, and Linux, and for the details of what supabase start brings up and how to access each service.

请记住,本地环境仅用于开发。它没有经过生产环境的加固,绝不能暴露给外部流量。它没有 TLS、没有速率限制,并使用默认凭据。可以用它来开发和测试,然后将其部署到 Supabase 平台 或合适的自托管环境,用于其他所有用途。

🌐 Keep in mind that the 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, then deploy to the Supabase Platform or a proper self-hosted setup for anything beyond that.

./supabase#

🌐 The ./supabase directory

supabase init 之后,你的项目会包含一个 ./supabase 目录。这里是它的内容以及需要提交的内容:

🌐 After supabase init, your project contains a ./supabase directory. Here's what goes in it and what to commit:

路径目的提交?
config.toml本地堆栈配置(端口、认证设置等)
migrations/按顺序执行的带时间戳 SQL 迁移文件
seed.sql开发/测试数据,在 startdb reset 的迁移之后应用
schemas/声明式模式文件(如果使用该方法)
.temp/.branches/CLI 内部状态

config.toml 是可以安全提交的。默认情况下它不包含任何机密信息。如果你添加了敏感信息(OAuth 凭证、API 密钥),请使用 env() 功能来引用环境变量,而不是硬编码它们。查看 管理配置和机密

🌐 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.

把现有项目移到本地开发 #

🌐 Move an existing project to local development

你已经在 Supabase 平台上搭建了一个项目,通过仪表板、SQL 编辑器或客户端库创建了表。现在你想要一个本地开发环境,并将所有内容纳入版本控制。

🌐 You've built a project on the Supabase platform, with tables created via the Dashboard, SQL editor, or client libraries. Now you want a local dev setup with everything in version control.

步骤 1:初始化 #

🌐 Step 1: Initialize

在你的项目根目录下:

🌐 In your project root:

1
supabase init

这会创建 ./supabase/config.toml。如果你已经有一个包含应用代码的项目目录,请在根目录运行这个命令。supabase/ 目录会和你的应用代码并列放置。

🌐 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.

步骤 2:认证 #

🌐 Step 2: Authenticate

1
supabase login

打开浏览器以生成访问令牌。该令牌会保存在本地,并用于所有后续与平台交互的 CLI 命令。

🌐 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

1
supabase link --project-ref <project-id>

在 Supabase 仪表板的 URL 中找到你的项目 ID:https://supabase.com/dashboard/project/<project-id>

🌐 Find your project ID in the Supabase Dashboard URL: https://supabase.com/dashboard/project/<project-id>.

这告诉命令行工具要连接哪个远程项目,以进行 db pulldb push 以及其他远程操作。系统会提示你输入数据库密码,也就是你创建项目时设置的密码。

🌐 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, which is the password set when you created the project.

第4步:拉取远程模式 #

🌐 Step 4: Pull the remote schema

1
supabase db pull

这会连接到你的远程数据库,导出整个架构,并将其保存为迁移文件:

🌐 This connects to your remote database, dumps the entire schema, and saves it as a migration file:

1
supabase/migrations/<timestamp>_remote_schema.sql

这个初始迁移就是你的基线。它代表了你数据库的当前状态,所有未来的更改都会在它的基础上进行。db pull 还会在远程迁移历史(supabase_migrations.schema_migrations 表)中记录这个迁移已被应用,所以之后的 db push 不会再尝试重新应用它。

🌐 This initial migration is your baseline. It represents the current state of your database, and all future changes build on top of it. db pull also records this migration as already applied in the remote migration history (the supabase_migrations.schema_migrations table), so a later db push won't try to reapply it.

步骤5:创建种子数据 #

🌐 Step 5: Create seed data

你有两个选择:

🌐 You have two options:

选项A:从远程导出现有数据(然后清理一下):

1
supabase db dump --data-only --linked > supabase/seed.sql

选项B:手动编写种子数据(大多数项目推荐):

用 INSERT 语句创建 supabase/seed.sql,设置一个有用的本地开发状态:几个测试用户、示例数据等等。这通常比导出生产数据更好,因为你可以完全控制里面的内容。

🌐 Create supabase/seed.sql with INSERT statements that set up a useful local development state: a few test users, sample data, and so on. This is often better than dumping production data because you control exactly what's in it.

想了解更多关于组织种子文件、glob 模式以及生成真实数据的信息,请参见 给你的数据库添加种子

🌐 For more on organizing seed files, glob patterns, and generating realistic data, see Seeding your database.

第6步:核实 #

🌐 Step 6: Verify

1
supabase start
2
supabase db reset

db reset 会销毁本地数据库并从头重新创建:它会按顺序应用所有迁移,然后运行 seed.sql。如果成功,你的环境就是可复现的。任何克隆该仓库的人都可以这样做。

第7步:提交 #

🌐 Step 7: Commit

1
git add supabase/
2
git commit -m "add supabase local development setup"

你的项目现在有了一个完全可复现的本地开发环境。

🌐 Your project now has a fully reproducible local development environment.

从零开始一个新项目 #

🌐 Start a new project from scratch

还没有远程项目。你是从零开始构建,想从一开始就做好。

🌐 No remote project yet. You're building from scratch and want to do it right from the start.

步骤 1:初始化 #

🌐 Step 1: Initialize

1
supabase init

第2步:启动本地堆栈 #

🌐 Step 2: Start the local stack

1
supabase start

首次运行时,会拉取 Docker 镜像,这需要几分钟。之后启动就很快了。运行后,CLI 会输出本地服务的 URL 和凭证,包括本地 Dashboard 实例的 Studio URL。有关完整输出和如何访问每个服务,请参见 安装和运行 CLI

🌐 On first run, Docker images are pulled, which takes a few minutes. Subsequent starts are fast. Once running, the CLI outputs local service URLs and credentials, including the Studio URL for a local instance of the Dashboard. See Install and run the CLI for the full output and how to reach each service.

步骤 3:创建你的模式 #

🌐 Step 3: Create your schema

两种方法,选一个:

🌐 Two approaches, pick one:

选项 A:声明式模式(推荐用于新项目)

将你希望数据库达到的状态声明为 supabase/schemas/ 中的一个文件,例如:

🌐 Declare the state you want your database to be in as a file in supabase/schemas/, for example:

1
create table public.todos (
2
id bigint generated by default as identity primary key,
3
created_at timestamptz default now() not null,
4
title text not null,
5
is_complete boolean default false not null,
6
user_id uuid references auth.users (id) default auth.uid() not null
7
);
8
9
alter table public.todos enable row level security;
10
11
create policy "Users can read their own todos"
12
on public.todos for select
13
using (auth.uid() = user_id);
14
15
create policy "Users can create their own todos"
16
on public.todos for insert
17
with check (auth.uid() = user_id);

然后从它生成一个迁移:

🌐 Then generate a migration from it:

1
supabase db diff -f initial-schema

这会将你声明的模式与当前(空)数据库进行比较,并在 supabase/migrations/ 中生成一个迁移文件。有关完整的声明式工作流程,包括管理视图和函数、排序模式文件以及已知注意事项,请参见 声明式数据库模式

🌐 This compares your declared schema against the current (empty) database and generates a migration file in supabase/migrations/. For the full declarative workflow, including managing views and functions, ordering schema files, and known caveats, see Declarative database schemas.

选项 B:直接编写迁移

1
supabase migration new initial-schema

这会在 supabase/migrations/<timestamp>_initial-schema.sql 创建一个空文件。在里面写你的 SQL,然后执行:

1
supabase db reset

步骤4:添加种子数据 #

🌐 Step 4: Add seed data

创建 supabase/seed.sql

🌐 Create supabase/seed.sql:

1
-- Create a test user (Supabase Auth)
2
-- Note: this is a placeholder row so seeded data has a user_id to reference.
3
-- It has no password, so it can't be used to sign in. To create a
4
-- login-capable user, use the Auth admin API or the local Studio.
5
insert into auth.users (id, email, raw_user_meta_data)
6
values ('d0e3c8f0-1234-5678-9abc-def012345678', 'test@example.com', '{}');
7
8
-- Seed application data
9
insert into public.todos (title, user_id)
10
values
11
('Buy groceries', 'd0e3c8f0-1234-5678-9abc-def012345678'),
12
('Write documentation', 'd0e3c8f0-1234-5678-9abc-def012345678');

步骤5:核实 #

🌐 Step 5: Verify

1
supabase db reset

清空一切,应用迁移,运行种子。如果通过了,你的项目就是可复现的。

🌐 Drops everything, applies migrations, runs seed. If this passes, your project is reproducible.

第6步:提交 #

🌐 Step 6: Commit

1
git add supabase/
2
git commit -m "add supabase local development setup"

日常工作流程 #

🌐 The daily workflow

两个起点在这里汇合。你的仓库里已经有一个可用的 ./supabase 目录。日常开发就是这样进行的。

🌐 Both starting points converge here. You have a working ./supabase directory in your repo. Here's how day-to-day development works.

进行模式更改 #

🌐 Making schema changes

你使用哪种方法是项目层面的决定,在你第一次创建模式时就已经确定了——不是每次更改时的选择。这取决于你是否在 supabase/schemas/ 中保留声明性文件。选择与你的项目匹配的标签。

🌐 Which approach you use is a project-level decision, set when you first created your schema - not a per-change choice. It depends on whether you keep declarative files in supabase/schemas/. Pick the tab that matches your project.

  1. supabase/schemas/ 中编辑你的 schema 文件(添加表、列、策略等)
  2. 生成一个迁移:supabase db diff -f add-due-date-to-todo
  3. 查看生成的迁移文件。请参阅 清理生成的迁移
  4. 验证完整链条:supabase db reset
  5. 把 schema 文件和迁移一起提交

生成类型 #

🌐 Generating types

如果你的应用使用了生成的 TypeScript 类型,每当你的模式发生变化时都要重新生成它们:

🌐 If your app uses the generated TypeScript types, regenerate them whenever your schema changes:

1
supabase gen types --lang typescript --local > database.types.ts

使用 --linked 而不是 --local 从你的远程项目生成。TypeScript 是默认语言;如果是其他语言,可以传入 --lang go--lang swift--lang python

🌐 Use --linked instead of --local to generate from your remote project. TypeScript is the default language; pass --lang go, --lang swift, or --lang python for others.

关于使用生成的类型(辅助类型、JSON 推断、类型安全查询)以及在 CI 中自动重新生成,请参阅 生成类型

🌐 For working with the generated types (helper types, JSON inference, type-safe queries) and automating regeneration in CI, see Generating types.

与团队保持同步 #

🌐 Staying in sync with your team

当别人推送新的迁移时:

🌐 When someone else pushes new migrations:

1
git pull
2
supabase db reset

db reset 会从头重放所有迁移,因此你总是会与仓库的当前状态保持一致。

推送到远程项目 #

🌐 Pushing to a remote project

当你准备好将你的模式部署到远程 Supabase 实例时:

🌐 When you're ready to deploy your schema to a remote Supabase instance:

1
# Authenticate (if not already)
2
supabase login
3
4
# Link to the remote project (if not already)
5
supabase link --project-ref <project-id>
6
7
# Preview what will be applied
8
supabase db push --dry-run
9
10
# Apply migrations
11
supabase db push

db push 只会应用那些还没在远程应用的迁移。它通过远程数据库上自动创建的 supabase_migrations.schema_migrations 表来跟踪这些迁移。

要同时初始化一个新的远程实例(仅限开发/预发布环境):

🌐 To also seed a fresh remote instance (dev/staging environments only):

1
supabase db push --include-seed

重置远程开发或预发布项目 #

🌐 Resetting a remote dev or staging project

如果开发或预发布环境的远程库出现偏差或变得混乱,你可以清空它,然后从本地迁移重新构建:

🌐 If a dev or staging remote drifts or gets into a messy state, you can wipe it and rebuild it from your local migrations:

1
supabase db reset --linked

不同于默认的 supabase db reset(它针对你的本地数据库),--linked 标志会针对你通过 supabase link 连接的远程项目:它会删除远程的 schema,然后按顺序重新执行每个本地迁移。加上 --include-seed 还能重新加载种子数据。

🌐 Unlike the default supabase db reset, which targets your local database, the --linked flag runs against the remote project you connected with supabase link: it drops the remote schema, then replays every local migration in order. Add --include-seed to reload seed data as well.

对于带有 CI/CD 的多环境设置(功能分支、预发布、生产环境),请参阅 管理环境

🌐 For multi-environment setups with CI/CD (feature branches, staging, production), see Managing Environments.

快捷键一览 #

🌐 Key commands at a glance

命令它的功能
supabase init创建 ./supabase/config.toml
supabase start启动本地栈,应用迁移 + 种子数据
supabase stop停止本地堆栈(数据会一直保留到 db reset
supabase db reset销毁本地数据库,重新执行所有迁移并从头导入初始数据
supabase db reset --linked销毁 关联的远程 数据库并从本地迁移重建它(有破坏性;仅限开发/测试环境)
supabase db diff -f <name>通过比较当前数据库状态与影子数据库生成迁移
supabase db pull将远程架构拉取到新的本地迁移文件中
supabase db push将待处理的本地迁移应用到远程数据库
supabase db dump通过 pg_dump 导出远程数据库架构(或者用 --data-only 导出数据)
supabase migration new <name>创建一个空的迁移文件
supabase migration list将本地迁移与远程迁移历史进行比较
supabase gen types --lang typescript从你的数据库模式生成 TypeScript 类型
supabase link --project-ref将本地项目连接到远程 Supabase 项目
supabase login使用 Supabase 平台进行身份验证

要查看完整的命令参考和所有标志,请参阅 CLI 参考

🌐 For the full command reference and every flag, see the CLI reference.

清理生成的迁移文件 #

🌐 Cleaning up generated migrations

supabase db diff 生成迁移时,它可能包含技术上正确但冗余的语句。提交之前请先检查每个生成的迁移。

🌐 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:

1
GRANT MAINTAIN, REFERENCES, TRIGGER, TRUNCATE ON public.todos TO anon;
2
GRANT MAINTAIN, REFERENCES, TRIGGER, TRUNCATE ON public.todos TO authenticated;
3
GRANT MAINTAIN, REFERENCES, TRIGGER, TRUNCATE ON public.todos TO service_role;

这些出现是因为 diff 工具把权限当作模式状态的一部分。对于 public 模式中的表,这些授权默认会应用,所以这些行是多余的。它们无害,但如果你想让迁移干净,可以删除它们。在团队中要保持一致,决定是保留还是删除这些行。

🌐 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:

1
REVOKE ALL ON TABLE public.todos FROM anon;
2
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 ... 可能会出现。如果你的迁移需要这个扩展,请保留它。如果这个扩展已经由之前的迁移创建,或是默认 Supabase 设置的一部分,则可以删除它。

db diff#

🌐 Known limitations of db diff

这个 diff 是由 pg-delta 生成的,它是默认的 schema diff 引擎。(较老的 migra 引擎仍然可用:在 config.toml 里设置 [experimental.pgdelta] 下的 enabled = false,或者通过 --use-migra 传入。)没有哪个 diff 引擎能捕捉到所有内容。最值得注意的是,DML(INSERT、UPDATE、DELETE)不会被跟踪,所以数据更改必须手动添加到迁移中,而且一些实体,比如 RLS 策略重命名和某些视图属性,也不能干净地生成 diff。完整的注意事项列表请参见声明式 schema 指南中的 完整列表

🌐 The diff is generated by pg-delta, the default schema diff engine. (The older migra engine is still available: set enabled = false under [experimental.pgdelta] in config.toml, or pass --use-migra.) No diff engine captures everything. Most notably, DML (INSERT, UPDATE, DELETE) is not tracked, so data changes must be added to the migration manually, and some entities like RLS policy renames and certain view properties don't diff cleanly. See the full list of caveats in the declarative schemas guide.

db diff 的输出当作草稿,而不是最终迁移。如有疑问,查看生成的 SQL 并手动调整。

🌐 Treat db diff output as a draft, not a final migration. When in doubt, review the generated SQL and adjust it manually.

故障排除 #

🌐 Troubleshooting

db reset 因迁移错误而失败

输出会显示哪个迁移文件失败以及 SQL 错误。修复迁移文件后,再次运行 db reset

🌐 The output will show which migration file failed and the SQL error. Fix the migration file, then run db reset again.

db push 表示迁移已经应用

远程数据库的历史里已经有那些迁移了。运行 supabase migration list 来比较本地和远程状态。如果它们不同步,使用 supabase migration repair 来纠正远程历史。

🌐 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.

模式漂移:远程在迁移之外被更改

如果有人直接修改了远程数据库(通过仪表盘、SQL 编辑器等),运行 supabase db pull 将这些更改捕获为新的迁移文件。然后在本地运行 supabase db reset 来验证一切是否仍然正常。

🌐 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.

supabase start上的Docker问题

确保 Docker 正在运行,并分配了至少 7 GB 的内存。如果容器健康检查失败,尝试:

🌐 Ensure Docker is running and has at least 7 GB of RAM allocated. If containers fail health checks, try:

1
supabase stop
2
supabase start

如果问题持续,使用 supabase stop --no-backup 进行彻底重启(这会删除本地数据库数据)。

🌐 If problems persist, supabase stop --no-backup for a clean restart (this removes local database data).