反问一句:你准备动手时,是先去官网点"新建项目",还是先在本地把整套环境拉起来?两种路线 Supabase 都支持,但本地优先能让你后面的每一次迁移都可在本机验证、再推生产,避免"本地能跑线上炸"。这一节我们把从零到第一个能连上的客户端走通,并讲清本地与云端的同构关系。
db push 和 functions deploy 同步到云端。好处是环境同构、离线可开发、迁移可评审。我们推荐本地优先,尤其团队项目。下面是从安装到起栈的完整会话:
# 1. 全局装 CLI npm install -g supabase # 2. 在项目目录初始化(生成 supabase/ 配置目录) supabase init # 3. 启动本地栈(首次会拉 Docker 镜像,稍慢) supabase start # service_role: eyJhbGciOi...(管理员密钥,勿外泄)
注意 anon key 和 service_role 都打在终端里。anon 可以进代码仓库的前端配置;service_role 等于绕过 RLS 的数据库管理员,绝不能进前端。
拿到 URL 和 anon key,前端就能连上了。下面是一段最小可跑的 TypeScript:
import { createClient } from '@supabase/supabase-js' const URL = 'http://127.0.0.1:54321' const ANON = 'eyJhbGciOi...' // 来自 supabase start 输出 export const supabase = createClient(URL, ANON) // 验证连通:查一次当前用户(未登录应为 null) const { data } = await supabase.auth.getUser() console.log('当前用户:', data.user) // 输出 当前用户: null
跑这段,控制台打印 当前用户: null 就说明客户端和本地栈连上了。
关键认知:本地 Docker 栈用的就是和云端相同的开源组件镜像。所以你在本地写的迁移、策略、函数,推到云端不会因为"环境不一样"出问题。下面 SVG 画了这种同构——同一份代码,两个运行环境:

背景:本地开发完一个带 profiles 表的小应用,要在云端建生产项目并迁移过去。
操作过程:
supabase login supabase link --project-ref <你的云端项目id> # 提示已关联
supabase db push # Applying migration 20240101000000_create_profiles.sql ... ok
select count(*) from profiles,返回 0(结构在,数据未搬,符合预期)。结果:结构完整上云,且和本地一致。数据迁移若需要,可单独用 pg_dump 导出再导入,不属于结构迁移范畴。
解读:同构的价值在这里兑现——本地验证过的策略,上云不用重调。若当初是"云端优先"在浏览器点出来的结构,反而没有这份可推送的迁移,上云时容易漏。
变式:多人协作时,每个人本地 supabase start 起独立栈,靠 Git 里的迁移文件同步结构,互不干扰,这正是本地优先对团队的意义。
⚠️ 不要把 service_role 密钥写进任何前端打包产物。它绕过 RLS,进前端等于把数据库裸给全网。真要在前端做管理员操作,走带服务端校验的 Edge Function。
💡 我们建议项目一开始就 supabase init 并提交 supabase/ 目录到 Git。迁移文件就是你的数据库源码,比任何"我在 Dashboard 点过"都可靠。
本地起栈看起来一行命令,但新人常在几个位置绊倒。下面把高频卡点和对应排查列成表,省得你对着黑屏发呆:
| 现象 | 常见根因 | 怎么办 |
|---|---|---|
supabase start 长时间不动 |
Docker 镜像首次拉取慢 / 没装 Docker | 确认 Docker 在跑;首次耐心等;检查网络 |
| 起栈后连不上 54321 | 端口被占用 | 改 config.toml 里的端口,或释放占用进程 |
anon key 复制不全导致 401 |
终端输出被截断 | 用 supabase status 重新完整打印 |
| 本地能连、云端连不上 | 云端项目没 db push 结构 |
先 link 再 db push |
| CLI 报版本过旧 | 全局装的版本落后 | npm install -g supabase 升级 |
💡 关键直觉:本地栈本质是一组 Docker 容器,所有"连不上"的问题,九成是容器没起来或端口不对。先用 supabase status 确认各服务 URL 真实可访问,再怀疑代码——顺序反了会白忙活。
背景:团队来了新人,按文档 supabase init 后 supabase start 一直报端口冲突,卡了半天没进展。
操作过程:
supabase status,发现 REST 端口 54321 被另一个本地 Postgres 占着。supabase/config.toml,把 API 端口改到 54331,Studio 改到 54333:# supabase/config.toml 片段 [api] port = 54331 # Studio 端口在 [studio] 段 [studio] port = 54333
supabase start,status 显示新端口正常监听。结果:半小时解决,新人当天跑通第一个查询。
解读:端口冲突是本地协作最常见的摩擦,但它和外部代码无关。把"改 config.toml 端口"当成标准排错第一步,比重装环境高效得多。我们也建议团队在 README 里写明"若默认端口被占,改成 5433x 段"的约定。
⚠️ 常见坑:有人为图省事把 config.toml 改了端口却没同步前端配置,结果栈起来了、前端还是连旧的 54321,报一连串 404。改端口务必前后端一起改,并写进项目说明。
下一节我们写真正的增删改查,并讲清 supabase-js 的查询语法和 RLS 如何共同保证安全。