Supabase
核心原则
1. Supabase 变化频繁——请在实施前对照更新日志和当前文档进行验证。 不要依赖训练数据中的 Supabase 功能特性。函数签名、config.toml 设置和 API 约定会随版本变化。
首先,获取 https://supabase.com/changelog.md(一个轻量级的摘要索引——非密集拉取),扫描与你的任务相关的 breaking-change 标签,并按照链接页面进行相应处理。然后使用下面的文档访问方法查找相关主题。
2. 验证你的工作。 在实施任何修复后,运行一次测试查询以确认更改生效。未经验证的修复是不完整的。
3. 从错误中恢复,而非循环重试。 如果某个方法在尝试 2-3 次后仍然失败,请停下来重新考虑。尝试不同的方法,查阅文档,更仔细地检查错误,并在可用时查看相关日志。Supabase 的问题并不总是通过重试相同命令就能解决的,答案也不总是在日志中,但在继续操作之前,日志通常值得检查。
4. 向数据 API 公开表: 根据用户的 Data API 设置,新创建的表可能不会自动通过 Data (REST) API 公开。如果是这种情况,则需要显式授予 anon 和 authenticated 角色访问权限。
请注意,这与 RLS 无关,RLS 控制的是表可访问后哪些_行_可见,而非表本身是否可访问。
当用户报告通过 SQL 创建的表意外无法访问时,请检查其 Data API 设置,以及是否通过显式的 GRANT SQL 授予了角色访问权限。在授予公共(anon/authenticated)访问权限时,务必同时启用 RLS。完整设置工作流程请参阅 向数据 API 公开表。
5. 公开模式中的 RLS。
在任何公开模式(默认包含 public)的每个表上启用 RLS。这在 Supabase 中至关重要,因为当 anon/authenticated 角色具有访问权限时,公开模式中的表可以通过 Data API 访问(请参阅 向数据 API 公开表)。对于私有模式,推荐将 RLS 作为深度防御。启用 RLS 后,创建与实际访问模型匹配的策略,而不是让每个表都默认使用相同的 auth.uid() 模式。
6. 安全检查清单。 在处理任何涉及认证、RLS、视图、存储或用户数据的 Supabase 任务时,请运行此检查清单。这些是 Supabase 特有的安全陷阱,会静默地创建漏洞:
-
认证和会话安全
- 切勿在基于 JWT 的授权决策中使用
user_metadata声明。 在 Supabase 中,raw_user_meta_data是用户可编辑的,并且可以出现在auth.jwt()中,因此它对于 RLS 策略或任何其他授权逻辑是不安全的。请改用raw_app_meta_data/app_metadata存储授权数据。 - 删除用户不会使现有的访问令牌失效。 首先注销或撤销会话,对敏感应用保持 JWT 短过期时间,并且为严格保证起见,在敏感操作上根据
auth.sessions验证session_id。 - 如果你使用
app_metadata或auth.jwt()进行授权,请记住 JWT 声明在用户令牌刷新之前并不总是最新的。
- 切勿在基于 JWT 的授权决策中使用
-
API 密钥和客户端暴露
- 切勿在公共客户端中暴露
service_role或密钥。 前端代码应优先使用可发布密钥。遗留的anon密钥仅用于兼容性。在 Next.js 中,任何NEXT_PUBLIC_环境变量都会发送到浏览器。 - 对管理 API、CLI 和 MCP 服务器使用范围限定的个人访问令牌。 经典的个人访问令牌携带用户对所有组织和项目的完整账户访问权限。对于脚本、CI 和智能体,请让用户创建一个范围限定令牌,仅限制任务所需的组织、项目和权限。个人访问令牌指南列出了每个管理 API 端点和 MCP 工具所需的权限。请在那里查找权限,而不是猜测。
- 切勿在公共客户端中暴露
-
RLS、视图和特权数据库代码
- 默认情况下,视图会绕过 RLS。 在 Postgres 15 及以上版本中,使用
CREATE VIEW ... WITH (security_invoker = true)。在较旧版本的 Postgres 中,通过撤销anon和authenticated角色的访问权限,或将它们放在未公开的模式中来保护你的视图。 - UPDATE 需要 SELECT 策略。 在 Postgres RLS 中,UPDATE 需要先 SELECT 该行。没有 SELECT 策略,更新会静默地返回 0 行——没有错误,只是没有更改。
auth.role()已弃用——请改用TO子句。 Supabase 已弃用auth.role(),转而支持直接在策略上使用TO authenticated或TO anon指定目标角色。除了弃用之外,当启用匿名登录时,auth.role() = 'authenticated'会静默地失效,因为匿名用户携带authenticatedPostgres 角色,无论用户是否真正登录都会通过检查。-- 已弃用(请勿使用) create policy "example" on table_name for select using ( auth.role() = 'authenticated' );- 单独使用
TO authenticated是身份验证而非授权(BOLA / IDOR)。 仅使用TO authenticated检查角色——它不会限制用户可以访问哪些行。正确的模式是将TO authenticated与USING中的所有权谓词结合:create policy "example" on table_name for select to authenticated using ( (select auth.uid()) = user_id ); - UPDATE 策略需要
USING和WITH CHECK两者。 没有WITH CHECK,用户可以将行的user_id重新分配给另一个用户:create policy "example" on table_name for update to authenticated using ( (select auth.uid()) = user_id ) with check ( (select auth.uid()) = 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)时,将函数保留在未公开的模式中,始终在函数体内包含一个auth.uid()检查,并在进行更改后运行supabase db advisors。
- 默认情况下,视图会绕过 RLS。 在 Postgres 15 及以上版本中,使用
-
存储访问控制
- 存储 upsert 需要 INSERT + SELECT + UPDATE。 仅授予 INSERT 允许新上传,但文件替换(upsert)会静默失败。你需要所有三个权限。
-
依赖和供应链安全
- 安装 Supabase 包(
supabase-js、@supabase/ssr、supabase-py等)时,始终固定包版本并提交锁定文件。 完整清单请参阅 npm 安全指南。
- 安装 Supabase 包(
对于上述未涵盖的任何安全顾虑,请获取 Supabase 产品安全索引:https://supabase.com/docs/guides/security/product-security.md
Supabase CLI
始终通过 --help 发现命令——切勿猜测。CLI 结构在不同版本之间会变化。
supabase --help # 所有顶级命令
supabase <group> --help # 子命令(例如,supabase db --help)
supabase <group> <command> --help # 特定命令的标志
Supabase CLI 已知陷阱:
supabase db query需要 CLI v2.79.0+ → 使用 MCPexecute_sql或psql作为回退方案supabase db advisors需要 CLI v2.81.3+ → 使用 MCPget_advisors作为回退方案supabase login的浏览器流程会创建一个具有完全账户访问权限的经典令牌。在 CI 中,请改用SUPABASE_ACCESS_TOKEN设置一个范围限定的个人访问令牌。它优先于任何已保存的登录。使用数据库密码(--password或SUPABASE_DB_PASSWORD,例如supabase db push)进行连接的命令不受令牌权限限制。- 在命令式迁移项目中,首先使用
supabase migration new <name>创建由手动编写的新迁移文件。切勿凭空编造迁移文件名或依赖记忆来获取预期格式。声明式模式项目从supabase/schemas/生成迁移;请参阅下面的“进行和提交模式更改”。
版本检查和升级: 运行 supabase --version 进行检查。有关 CLI 更新日志和特定版本的功能,请查阅 CLI 文档 或 GitHub 发布页面。
Supabase MCP 服务器
有关设置说明、服务器 URL 和配置,请参阅 MCP 设置指南。
故障排除连接问题——请按顺序执行以下步骤:
-
检查服务器是否可访问:
curl -so /dev/null -w "%{http_code}" https://mcp.supabase.com/mcp401是预期的(无令牌),表示服务器已启动。超时或“connection refused”表示它可能已关闭。 -
检查
.mcp.json配置: 验证项目根目录包含一个有效的.mcp.json,且具有正确的服务器 URL。如果缺失,请创建一个指向https://mcp.supabase.com/mcp的文件。 -
认证 MCP 服务器: 如果服务器可达且
.mcp.json正确,但工具不可见,则用户需要进行身份验证。Supabase MCP 服务器使用 OAuth 2.1——告诉用户在其智能体中触发认证流程,在浏览器中完成,然后重新加载会话。 在 CI 或无法运行浏览器流程的其他环境中,改为在Authorization: Bearer头部中传递一个范围限定的个人访问令牌。 -
检查范围限定令牌的权限: 如果使用范围限定的个人访问令牌时工具失败并显示“You do not have permission to perform this action”,则说明该令牌缺少该工具的权限,或者项目超出了令牌的范围。在 个人访问令牌指南 中检查该工具所需的权限。
Supabase 文档
在实施任何 Supabase 功能之前,请找到相关文档。按优先级顺序使用以下方法:
- MCP
search_docs工具(首选——直接返回相关片段) - 将文档页面作为 markdown 获取——任何文档页面都可以通过将
.md附加到 URL 路径来获取。 - 对 Supabase 特定主题进行网络搜索,当你不知道应查看哪个页面时。
进行和提交模式更改
首先确定项目使用哪种模式工作流程。
选项 A:声明式模式
当 supabase/schemas/ 存在或 config.toml 设置了 schema_paths 时使用此选项。编辑这些文件中的所需模式状态,然后生成并审查迁移。不要从头开始手写迁移。请参阅 声明式数据库模式指南。
选项 B:命令式迁移
当项目不使用声明式模式时使用此选项。
要进行模式更改,请使用 execute_sql(MCP)或 supabase db query(CLI)。 这些命令直接在数据库上运行 SQL,而不会创建迁移历史记录条目,因此你可以自由迭代,并在准备就绪时生成一个干净的迁移。
请勿使用 apply_migration 来更改本地数据库模式——它每次调用都会写入一个迁移历史记录条目,这意味着你无法迭代,并且 supabase db diff / supabase db pull 将产生空的或冲突的差异。如果你使用它,你将受困于第一次尝试时传递的任何 SQL。
准备就绪后,将你的更改提交到迁移文件:
- 运行顾问 →
supabase db advisors(CLI v2.81.3+)或 MCPget_advisors。修复任何问题。 - 如果更改涉及视图、函数、触发器等,请复查上面的安全检查清单。
- 生成迁移 →
supabase db pull <描述性名称> --local --yes - 验证 →
supabase migration list --local
调试
当你在 Supabase 相关请求上遇到错误时,例如来自 Supabase REST API、Postgres 数据库或 PostgREST 的错误代码、空结果、意外被 RLS 阻止,或来自 Auth、Realtime、Edge Functions 或 Storage 等 Supabase 服务的错误,你必须获取 Supabase 的 监控和调试 文档,然后再进行诊断或提出修复方案,而不是凭记忆工作。相同的文档也涵盖了性能优化,例如慢查询和缺失索引。
参考指南
- 技能反馈 → references/skill-feedback.md 当用户报告此技能给出了错误指导或缺少信息时,必须阅读此文件。