Supabase
核心原则
1. Supabase 经常变化——在实现之前,请对照 changelog 和当前文档进行验证。 不要依赖训练数据来处理 Supabase 功能。函数签名、config.toml 设置和 API 约定会在不同版本之间变化。
首先,获取 https://supabase.com/changelog.md(一个轻量级摘要索引——不是重型拉取),扫描与你的任务相关的 breaking-change 标签,并跟随适用的链接页面。然后使用下面的文档访问方法查找相关主题。
2. 验证你的工作。 实现任何修复后,运行一个测试查询以确认更改有效。未经过验证的修复是不完整的。
3. 从错误中恢复,不要循环。 如果一种方法在 2-3 次尝试后失败,停止并重新考虑。尝试不同方法,查看文档,更仔细地检查错误,并在可用时查看相关日志。Supabase 问题并不总是通过重试同一命令解决,答案也不总是在日志中,但在继续之前,日志通常值得检查。
4. 将表暴露给 Data API: 根据用户的 Data API settings,新创建的表可能不会自动通过 Data (REST) API 暴露。如果发生这种情况,anon 和 authenticated 角色需要被显式授予访问权限。
请注意,这与 RLS 不同;RLS 控制表可访问后哪些 _行_ 可见,而不是表是否可访问。
当用户报告通过 SQL 创建的表意外不可访问时,检查他们的 Data API 设置,以及是否通过显式 GRANT SQL 授予角色访问权限。当授予公共(anon/authenticated)访问权限时,始终同时启用 RLS。参见 Exposing a Table to the Data API 了解完整设置工作流。
5. 暴露 schema 中的 RLS。 在任何暴露 schema 中的每个表上启用 RLS,默认包括 public。这在 Supabase 中至关重要,因为当 anon/authenticated 角色有访问权限时,暴露 schema 中的表可以通过 Data API 访问(参见 Exposing a Table to the Data API)。对于私有 schema,优先将 RLS 作为纵深防御。启用 RLS 后,创建匹配实际访问模型的政策,而不是将每个表默认设置为相同的 auth.uid() 模式。
6. 安全检查清单。 在处理任何涉及 auth、RLS、视图、存储或用户数据的 Supabase 任务时,运行此检查清单。这些是 Supabase 特有的安全陷阱,会静默创建漏洞:
- 认证与会话安全
- 绝不在基于 JWT 的授权决策中使用
user_metadataclaims。 在 Supabase 中,raw_user_meta_data可由用户编辑,并可能出现在auth.jwt()中,因此对于 RLS 政策或任何其他授权逻辑都不安全。改为将授权数据存储在raw_app_meta_data/app_metadata中。 - 删除用户不会使现有访问令牌失效。 先注销或撤销会话,为敏感应用保持较短的 JWT 过期时间,并且对于严格保证,在敏感操作中将
session_id与auth.sessions进行验证。 - 如果你使用
app_metadata或auth.jwt()进行授权,请记住 JWT claims 在用户令牌刷新之前并不总是最新的。
- API key 与客户端暴露
- 绝不在公共客户端中暴露
service_role或 secret key。 前端代码优先使用 publishable keys。旧版anonkeys 仅用于兼容性。在 Next.js 中,任何NEXT_PUBLIC_环境变量都会发送到浏览器。
- RLS、视图和特权数据库代码
- 视图默认绕过 RLS。 在 Postgres 15 及更高版本中,使用
CREATE VIEW ... WITH (security_invoker = true)。在较旧版本的 Postgres 中,通过撤销anon和authenticated角色的访问权限,或将视图放在未暴露 schema 中来保护你的视图。 - UPDATE 需要 SELECT 政策。 在 Postgres RLS 中,UPDATE 需要先 SELECT 行。如果没有 SELECT 政策,更新会静默返回 0 行——没有错误,只是没有更改。
auth.role()已弃用——请改用TO子句。 Supabase 已弃用auth.role(),改为在政策上直接使用TO authenticated或TO anon指定目标角色。除了弃用之外,当启用匿名登录时,auth.role() = 'authenticated'会静默失效,因为匿名用户携带authenticatedPostgres 角色,并且无论用户是否真正登录都会通过检查。- 单独使用
TO authenticated是身份认证但没有授权(BOLA / IDOR)。 使用TO authenticated仅检查角色——它不会限制用户可以访问哪些行。正确模式将TO authenticated与USING中的所有权谓词结合: - UPDATE 政策需要同时包含
USING和WITH CHECK。 如果没有WITH CHECK,用户可以将某行的user_id重新分配给另一个用户: SECURITY DEFINER函数会绕过 RLS。SECURITY DEFINER函数以其创建者的特权运行——通常是具有bypassrls的角色(例如postgres)。绝不要为了解决权限错误而添加SECURITY DEFINER;它会静默移除访问控制,而没有修复根本原因。优先使用SECURITY INVOKER。public中的SECURITY DEFINER函数可被所有角色调用。 Postgres 默认对每个新函数向PUBLIC授予EXECUTE,因此public中的任何SECURITY DEFINER函数都是一个公共 API 端点,anon和authenticated(它们继承自PUBLIC)无需额外授权即可调用。当确实需要SECURITY DEFINER时(例如绕过内部查找表上的 RLS),将函数保留在非暴露 schema 中,始终在函数体中包含auth.uid()检查,并在进行更改后运行supabase db advisors。
``sql -- Deprecated (do not use) create policy "example" on table_name for select using ( auth.role() = 'authenticated' ); ``
``sql create policy "example" on table_name for select to authenticated using ( (select auth.uid()) = user_id ); ``
``sql create policy "example" on table_name for update to authenticated using ( (select auth.uid()) = user_id ) with check ( (select auth.uid()) = user_id ); ``
- 存储访问控制
- Storage upsert 需要 INSERT + SELECT + UPDATE。 仅授予 INSERT 允许新上传,但文件替换(upsert)会静默失败。你需要全部三项。
- 依赖与供应链安全
- 在安装 Supabase 包时始终固定包版本并提交 lockfiles(
supabase-js、@supabase/ssr、supabase-py等)。参见 npm security guide 了解完整检查清单。
对于上面未涵盖的任何安全问题,获取 Supabase 产品安全索引:https://supabase.com/docs/guides/security/product-security.md
Supabase CLI
始终通过 --help 发现命令——绝不猜测。CLI 结构在不同版本之间会变化。
supabase --help # All top-level commands
supabase <group> --help # Subcommands (e.g., supabase db --help)
supabase <group> <command> --help # Flags for a specific command
Supabase CLI 已知陷阱:
supabase db query需要 CLI v2.79.0+ → 使用 MCPexecute_sql或psql作为回退supabase db advisors需要 CLI v2.81.3+ → 使用 MCPget_advisors作为回退- 在命令式 migration 项目中,先使用
supabase migration new <name>创建新的手写 migration 文件。绝不发明 migration 文件名或依赖记忆来猜测预期格式。声明式 schema 项目会从supabase/schemas/生成 migrations;参见下面的 "Making and Committing Schema Changes"。
版本检查和升级: 运行 supabase --version 进行检查。对于 CLI changelogs 和特定版本功能,请查阅 CLI documentation 或 GitHub releases。
Supabase MCP Server
有关设置说明、服务器 URL 和配置,请参见 MCP setup guide。
排查连接问题 — 按顺序执行以下步骤:
- 检查服务器是否可达:
curl -so /dev/null -w "%{http_code}" https://mcp.supabase.com/mcp 预期是 401(没有 token),这意味着服务器已启动。超时或 "connection refused" 表示它可能已宕机。
- 检查
.mcp.json配置:
验证项目根目录中是否存在有效的 .mcp.json,并且服务器 URL 正确。如果缺失,创建一个指向 https://mcp.supabase.com/mcp 的配置。
- 对 MCP 服务器进行身份验证:
如果服务器可达且 .mcp.json 正确,但工具不可见,用户需要进行身份验证。Supabase MCP 服务器使用 OAuth 2.1 —— 告诉用户在他们的 agent 中触发 auth 流程,在浏览器中完成,并重新加载会话。
Supabase 文档
在实现任何 Supabase 功能之前,找到相关文档。按优先顺序使用这些方法:
- MCP
search_docs工具(优先——直接返回相关片段) - 以 markdown 形式获取文档页面 — 任何文档页面都可以通过在 URL 路径后追加
.md来获取。 - 当你不知道应该查看哪个页面时,对 Supabase 特定主题进行 web search。
进行并提交 Schema 更改
首先决定项目使用哪种 schema 工作流。
选项 A:声明式 schemas
当存在 supabase/schemas/ 或 config.toml 设置了 schema_paths 时使用此方法。在这些文件中编辑所需的 schema 状态,然后生成并审查 migration。不要以手写 migration 开始。参见 Declarative database schemas guide。
选项 B:命令式 migrations
当项目不使用声明式 schemas 时使用此方法。
要进行 schema 更改,请使用 execute_sql(MCP)或 supabase db query(CLI)。 这些会直接在数据库上运行 SQL,而不会创建 migration history 条目,因此你可以自由迭代,并在准备就绪时生成干净的 migration。
切勿使用 apply_migration 更改本地数据库 schema——它每次调用都会写入一个 migration history 条目,这意味着你无法迭代,并且 supabase db diff / supabase db pull 会产生空或冲突的 diffs。如果你使用它,你将被困在第一次尝试时传入的 SQL 上。
当准备提交你的更改到 migration 文件时:
- 运行 advisors →
supabase db advisors(CLI v2.81.3+)或 MCPget_advisors。修复任何问题。 - 如果你的更改涉及视图、函数、触发器或存储,审查上面的安全检查清单。
- 生成 migration →
supabase db pull <descriptive-name> --local --yes - 验证 →
supabase migration list --local
调试
当你在 Supabase 相关请求中收到错误时,例如来自 Supabase REST API、Postgres 数据库或 PostgREST 的错误代码、空结果、意外被 RLS 阻止,或来自 Auth、Realtime、Edge Functions 或 Storage 等 Supabase 服务的错误,你必须获取 Supabase 的 Monitoring and Debugging 文档,然后再进行诊断或提出修复方案,而不是凭记忆工作。同一文档也涵盖性能优化,例如慢查询和缺失索引。
参考指南
- Skill Feedback → [references/skill-feedback.md](references/skill-feedback.md)
必须阅读,当用户报告此 skill 给出了错误指导或缺少信息时。