Supabase 后端即服务平台 Skill
Supabase 把权限问题伪装成了配置选择——service_role/sb_secret_... key 完全绕过 Row Level Security,一旦流入前端代码就等于把整个数据库开放给任何人。更隐蔽的是失败模式不对称:缺 grant 会报 42501 错误,而 RLS policy 把某一行过滤掉时不报错,只是安静地返回空结果,用 anon key 调用查询返回空时不能默认是"这个表本来没数据"。
anon/service_role(JWT)与 sb_publishable_/sb_secret_(短字符串),配合 Row Level SecurityAgent 工具更新 2026-09-21安装
选你用的 Agent,复制命令。
npx -y skills add fxp/skillify --skill supabase --agent claude-code --yes装到项目的 .claude/skills/;加 -g 装到全局。装完需要你手动执行 /reload-plugins,这一步 Agent 代劳不了。
npx -y skills add fxp/skillify --skill supabase --agent codex --yes装到项目的 .agents/skills/;加 -g 装到全局。
npx -y skills add fxp/skillify --skill supabase --agent cursor --yes装到项目的 .agents/skills/;加 -g 装到全局。
npx -y skills add fxp/skillify --skill supabase --agent opencode --yes装到项目的 .agents/skills/;加 -g 装到全局。
npx -y skills add fxp/skillify --skill supabase --agent gemini-cli --yes装到项目的 .agents/skills/;加 -g 装到全局。
npx -y skills add fxp/skillify --skill supabase --agent github-copilot --yes装到项目的 .agents/skills/;加 -g 装到全局。
npx -y skills add fxp/skillify --skill supabase --agent trae --yes装到项目的 .trae/skills/;加 -g 装到全局。
npx -y skills add fxp/skillify --skill supabase --agent windsurf --yes装到项目的 .windsurf/skills/;加 -g 装到全局。
npx -y skills add fxp/skillify --skill supabase --agent qwen-code --yes把 --agent 换成你用的 Agent 标识,可以一次写多个。skills CLI 1.5.25 共支持 79 个,例如 qwen-code、kimi-code-cli、trae-cn、qoder、lingma、cline、roo。
或者让 Agent 自己装
读 https://github.com/fxp/skillify/blob/main/skills/supabase/prompt.md 并照它执行装对了的标志:references/ 下有 6 份文档。
装好后,可以这样问你的 Agent
这份 Skill 覆盖的典型任务。点一下复制。
6 类能力
每类能力对应一份 reference,Agent 按任务只打开需要的那份。
REST API
PostgREST 查询语法(select/关联查询/filter)、分页计数、upsert、调用 Postgres 函数
鉴权与 RLS
key 类型与安全等级、grant 与 RLS 两道独立闸门、policy 写法、JWT 结构与 auth.uid()
Storage
bucket、标准上传、公开 URL vs 签名 URL、storage.objects 上的 RLS
Realtime
Postgres Changes 订阅、启用条件(publication+RLS)、DELETE 事件不受 RLS 保护、Broadcast/Presence 私有 channel
Edge Functions
Deno 运行时与 npm 依赖限制、鉴权模式(user/secret/publishable/none)、环境变量、资源限制
错误码与限制
PostgREST/Storage/Edge Functions 各自的错误码表、grant 与 RLS 失败模式的区分
当前事实
整理自官方文档(抓取于 2026-09-21),还没有用真实凭证验证。
| 项 | 值 |
|---|---|
| Data API | https://<project_ref>.supabase.co/rest/v1/,PostgREST 自动从表结构生成 REST 路由 |
| Key 分级 | 低权限 anon/sb_publishable_... 可放客户端;高权限 service_role/sb_secret_... 绝不能出现在前端 |
| 两个鉴权 header | apikey 标识项目/key;Authorization: Bearer 装用户会话 JWT,两者职责不同 |
| 过滤语法 | URL query string 的 列名=操作符.值(如 ?status=eq.active),不是请求体 JSON |
| Realtime 启用条件 | 表要加进 supabase_realtime publication,且调用方要能通过 RLS SELECT 读到该行,两者缺一不可 |
| Edge Functions | 跑在 Deno 上不是 Node,npm 包要带 npm: 前缀导入,原生编译依赖(如 sharp)不受支持 |
照通用经验写容易错的 6 件事
来自官方文档,还没有用真实凭证验证。
- key 选型是安全问题,不是配置细节。
service_role/sb_secret_...完全绕过 RLS,不是"权限更大的用户"而是"没有行级限制";跑在用户设备/浏览器上的代码一律只能用anon/publishable。 - 缺 grant 和 RLS 过滤是两种完全不同的失败模式。没有 grant 直接报
42501权限错误;有 grant 但策略把这一行过滤掉,不报错,返回 200 + 空数组——用 anon key 查询返回空不能默认"表本来没数据",要先查 RLS policy。 - 新版
sb_publishable_/sb_secret_key 必须放apikeyheader,放Authorization: Bearer会被当 JWT 解析失败。已登录用户的会话 JWT 才放Authorization: Bearer,两个 header 装的是不同的值,不能合并成一个。 - Realtime 的 DELETE 事件不受 RLS 保护,这是设计如此。Postgres 无法对"已经被删除的行"做权限判断,只要表加进了
supabase_realtimepublication,delete 事件会推给所有订阅者,不受该表 SELECT policy 限制——不能假设"RLS 保护了 select 就保护了 delete 通知"。 - PostgREST 的过滤条件是 URL query string,不是请求体 JSON。
GET /rest/v1/todos?id=eq.5&score=gte.10这种列名=操作符.值写法,和"在 body 里传 filter 对象"的常见 REST 习惯完全不同;没有过滤条件的 DELETE 会删掉该角色能看到的所有行。 - Data API 和 Storage 的"覆盖写入"语法互不相通。Data API 的 upsert 靠
Prefer: resolution=merge-duplicates头;Storage 的覆盖上传靠完全不同的x-upsert: trueheader(或客户端库的upsert: true参数),两套产品各自一套,别混用。