关于 api-baas-supabase
面向使用 Supabase 构建后端的开发者,整理其 Postgres、认证、实时订阅、文件存储和 Edge Functions 的开发模式。指南着重类型安全、RLS 数据访问策略与错误处理。
主要功能
- 01带 Database 泛型的类型安全客户端
- 02Postgres 查询与错误处理
- 03认证流程与会话管理
- 04RLS 策略与数据访问控制
- 05实时订阅、文件存储与 Edge Functions
适用场景
- 01使用 Supabase 客户端查询 Postgres
- 02实现 OAuth、魔法链接或会话管理
- 03为多用户数据配置 RLS 策略
- 04订阅数据库变更或上传文件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ agents-inc/ skills --skill api-baas-supabase安装后在 Claude Code 中输入/api-baas-supabase确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
metadata.yamlreference.mdexamples/auth.mdcore.mddatabase.mdedge-functions.mdstorage.md详细介绍
原文在翻译后有更新,译文可能不是最新。
Supabase 模式
**快速指南:**将 Supabase 用作后端即服务,提供 Postgres 数据库、身份验证、实时订阅、文件存储和 Edge Functions。始终使用带有
Database泛型的类型化客户端,在每个表上启用 RLS,并且仅在服务器上使用密钥。
<critical_requirements>
重要:使用此技能前
所有代码都必须遵循项目在 CLAUDE.md 中的约定(kebab-case、命名导出、导入顺序、
import type、命名常量)
(你必须在公开 schema 中的每个表上启用行级安全(RLS),没有例外)
(你必须在 createClient<Database>() 中使用 Database 泛型类型,以确保查询类型安全)
(你绝不能在客户端代码中暴露密钥——浏览器中使用可发布密钥,密钥仅在服务器上使用)
(你必须在 RLS 策略中使用封装在子查询内的 (select auth.uid()),以提升性能)
(你必须使用 { data, error } 解构来处理所有 Supabase 响应——绝不能假定操作成功)
</critical_requirements>
**自动检测:**Supabase、createClient、@supabase/supabase-js、@supabase/ssr、supabase-js、auth.uid()、RLS、行级安全、实时、postgres_changes、supabase.auth、supabase.from、supabase.storage、supabase.functions、supabase.channel、Edge Function、Deno.serve
适用场景:
- 使用 TypeScript 类型安全配置 Supabase 客户端
- 实现身份验证(电子邮件/密码、OAuth、魔法链接、会话管理)
- 通过 Supabase 客户端查询 Postgres(select、insert、update、delete、RPC)
- 编写用于数据访问控制的行级安全策略
- 实时订阅数据库变更
- 从 Supabase Storage 上传和提供文件
- 使用 Supabase Edge Functions(Deno)构建无服务器函数
涵盖的关键模式:
- 使用
Database泛型和环境变量配置类型化客户端 - 身份验证流程:注册、登录、OAuth、魔法链接、会话刷新、
onAuthStateChange - 使用筛选条件、联接、RPC 调用和错误处理来查询数据库
- RLS 策略:
USING与WITH CHECK、auth.uid()、基于角色的访问 - 通过
channel().on('postgres_changes')进行实时订阅 - 存储:上传、签名 URL、公开 URL、存储桶策略
- Edge Functions:
Deno.serve、CORS 标头、密钥、函数中的 Supabase 客户端
不适用场景:
- 直接连接 Postgres(请改用数据库驱动程序技能)
- 复杂的服务端 ORM 模式(请改用专用 ORM 技能)
- 非 Supabase 身份验证提供商(请改用专用身份验证技能)
详细资源:
- 有关决策框架和反模式,请参阅 reference.md
客户端与查询:
- examples/core.md — 客户端配置、类型化查询、错误处理模式
身份验证:
- examples/auth.md — 完整的身份验证流程、OAuth、魔法链接、会话刷新、中间件防护
数据库:
- examples/database.md — 复杂查询、联接、RPC、迁移、类型生成
存储:
- examples/storage.md — 文件上传、签名 URL、存储桶策略、图像转换
Edge Functions:
- examples/edge-functions.md — Deno Edge Functions、
Deno.serve()、CORS、密钥
## 理念
Supabase 是一个基于 Postgres 构建的开源 Firebase 替代方案。它通过组合 Postgres 扩展、自动生成的 REST/GraphQL API、身份验证、实时订阅、文件存储和边缘函数,提供完整的后端。
核心原则:
- 以 Postgres 为核心 — 每项功能都基于 Postgres 构建。RLS 策略、身份验证和实时功能都利用了 Postgres 原语。理解 Postgres,就是理解 Supabase。
- 端到端类型安全 — 使用
supabase gen types根据数据库架构生成 TypeScript 类型。将Database泛型传递给createClient,即可让查询拥有完整类型。 - 默认安全 — 必须为每张表启用 RLS。可发布密钥可安全用于浏览器(RLS 会强制执行访问控制)。密钥会绕过 RLS,绝不能离开服务器。
- 错误作为值 — 每个 Supabase 方法都会返回
{ data, error }。绝不要假定操作成功。使用data前始终检查error。 - 内置实时功能 — Postgres 更改通过通道经由 WebSocket 推送。无需单独的发布/订阅基础设施。
- 边缘优先函数 — 边缘函数在靠近用户的边缘运行 Deno。应针对短生命周期、幂等操作进行设计。
适用场景:
- 需要开箱即用的 Postgres、身份验证和存储,以快速开发后端
- 需要实时功能(聊天、通知、实时仪表板)的项目
- 希望避免分别管理身份验证、数据库和存储服务的团队
- 可利用行级安全实现多租户数据隔离的应用程序
不适用场景:
- 需要完整应用服务器来处理复杂服务端业务逻辑的应用程序(简单场景可使用边缘函数,复杂场景应使用专用 API)
- 需要具备高级查询构建功能的 ORM 的应用程序(Supabase 查询构建器功能强大,但并非完整 ORM)
- 需要复杂同步协议的离线优先应用程序
核心模式
模式 1:设置类型化客户端
始终将 Database 泛型传递给 createClient,以便对表名、列名和返回类型启用完整的自动补全。使用环境变量配置 URL 和密钥。
export const supabase = createClient<Database>(
SUPABASE_URL,
SUPABASE_PUBLISHABLE_KEY,
);
如果不使用泛型,表名或列名中的拼写错误就无法在编译时被发现。浏览器、服务器和管理员客户端的设置模式,请参阅 examples/core.md。
模式 2:使用 { data, error } 处理错误
每个 Supabase 方法都会返回 { data, error }。始终解构返回值,并在使用 data 前检查 error。不要对 data 使用非空断言。
const { data, error } = await supabase
.from("profiles")
.select("id, username")
.eq("id", userId)
.single();
if (error) throw new Error(`Failed to fetch profile: ${error.message}`);
可复用错误处理程序模式和常见错误,请参阅 examples/core.md。
模式 3:身份验证流程
Supabase Auth 支持电子邮件/密码(signInWithPassword)、OAuth(signInWithOAuth)、魔法链接(signInWithOtp)和手机 OTP。应在应用生命周期早期注册 onAuthStateChange,并始终使用 subscription.unsubscribe() 清理订阅。
需要特别注意:不要在 onAuthStateChange 内直接调用 Supabase 方法 — 应使用 setTimeout(..., 0) 延后执行。
注册、登录、OAuth、魔法链接、会话管理、中间件保护和密码重置模式,请参阅 examples/auth.md。
模式 4:数据库查询
使用查询构建器,通过筛选、联接、排序和分页执行类型安全的 CRUD 操作。在 .insert() 或 .update() 后始终添加 .select(),以返回受影响的行。
const { data, error } = await supabase
.from("posts")
.select("id, title, author:profiles(username)")
.eq("published", true)
.order("created_at", { ascending: false })
.range(0, PAGE_SIZE - 1);
复杂查询、upsert、RPC 调用、条件筛选、计数和迁移,请参阅 examples/database.md。
模式 5:行级安全(RLS)策略
RLS 是主要的安全机制。为每张表启用 RLS,为每种操作分别编写策略(不要使用 FOR ALL),并将 auth.uid() 包装在子查询中以提升性能。
alter table public.posts enable row level security;
create policy "posts_select" on public.posts for select to authenticated
using ( published = true or (select auth.uid()) = author_id );
切勿信任 JWT 中的 user_metadata 来控制访问权限,因为用户可以修改它。完整的 CRUD 策略、基于团队的访问控制和反模式示例,请参阅 examples/database.md。
模式 6:实时订阅
通过 channel().on('postgres_changes', ...) 订阅数据库变更。清理时务必取消订阅。DELETE 事件无法筛选——所有删除事件都会收到。UPDATE/DELETE 负载需要 replica identity full 才能包含旧记录数据。
const channel = supabase
.channel("room-messages")
.on(
"postgres_changes",
{
event: "INSERT",
schema: "public",
table: "messages",
filter: `room_id=eq.${roomId}`,
},
(payload) => {
/* handle */
},
)
.subscribe();
适用于聊天、实时仪表板和通知。避免用于高频数据(> 100 次更新/秒)。
模式 7:存储操作
使用 supabase.storage.from(bucket).upload() 上传文件。对于公共存储桶,使用 getPublicUrl();对于需要限时访问的私有存储桶,使用 createSignedUrl()。存储访问控制通过 storage.objects 上的 RLS 实现。
上传、签名 URL、公共 URL、图像转换、存储桶策略和签名上传 URL 示例,请参阅 examples/storage.md。
模式 8:边缘函数
使用 Deno.serve()(不要使用已弃用的 serve 导入)。使用 npm: 前缀导入 supabase-js:import { createClient } from "npm:@supabase/supabase-js@2"。为每个响应处理 CORS。使用 Deno.env.get() 获取密钥。转发用户 JWT 以强制执行 RLS。
基本函数、经过身份验证的访问、共享工具、Webhook、多路由“胖函数”,以及使用 EdgeRuntime.waitUntil() 进行后台处理的示例,请参阅 examples/edge-functions.md。
<decision_framework>
决策框架
选择哪个 Supabase 密钥
Where is the code running?
├─ Browser / Client-side → publishable key (RLS enforced)
├─ Server / API route → publishable key + user JWT (RLS enforced per user)
└─ Admin / Migration script → secret key (bypasses RLS)
└─ NEVER expose the secret key in client bundles
选择身份验证方式
What auth flow does the user need?
├─ Email + Password → signInWithPassword
├─ Social login (GitHub, Google, etc.) → signInWithOAuth
├─ Passwordless email → signInWithOtp (magic link)
├─ Phone + SMS → signInWithOtp (phone)
└─ SSO / SAML → signInWithSSO (enterprise)
实时订阅还是轮询
How fresh must the data be?
├─ Instant (< 1 second) → Realtime subscription (postgres_changes)
├─ Near-instant (1-5 seconds) → Realtime subscription
├─ Periodic (> 5 seconds ok) → Polling with setInterval
└─ On-demand (user refresh) → Re-fetch on action
└─ High-frequency updates (> 100/sec)?
├─ YES → Polling or batch (Realtime has per-subscriber checks)
└─ NO → Realtime is fine
存储:公共存储桶还是私有存储桶
Who should access the files?
├─ Anyone (public assets, avatars) → Public bucket + getPublicUrl()
├─ Authenticated users only → Private bucket + createSignedUrl()
├─ Specific users (own files) → Private bucket + RLS on storage.objects
└─ Server-only processing → secret key for upload/download
边缘函数还是客户端查询
Does the operation need server-side logic?
├─ Simple CRUD → Client query with RLS (no edge function needed)
├─ Multi-step / transactional → Edge function or Postgres function (RPC)
├─ Third-party API call → Edge function
├─ Webhook receiver → Edge function
└─ Heavy computation → Edge function with EdgeRuntime.waitUntil() for background work
</decision_framework>
<red_flags>
危险信号
高优先级问题:
- 表未启用 RLS — 暴露架构中的任何未启用 RLS 的表都对公众完全开放。2025 年 1 月,由于缺少 RLS,发现 170 多个应用的数据库暴露(CVE-2025-48757)。
- 客户端代码中包含密钥 — 密钥(以前称为
service_role密钥)会绕过所有 RLS。将其暴露在浏览器包中,会让每位用户都获得数据库的完整管理员访问权限。 - 忽略
{ data, error }返回值 — 不检查error就访问data,操作失败时会导致运行时崩溃。 - 在 RLS 策略中使用
auth.jwt() ->> 'user_metadata'— 已验证用户可以通过updateUser()修改user_metadata。切勿将其用于访问控制决策。
中优先级问题:
- 在 RLS 策略中使用
FOR ALL— 应拆分为SELECT、INSERT、UPDATE、DELETE策略,以便于理解和审计。 - 在策略中直接使用
auth.uid(),未使用子查询 — 用(select auth.uid())包裹,根据 Supabase 基准测试,性能最高可提升 94–99%。 - 策略中未指定
to authenticated或to anon— 未指定角色时,策略会应用于所有角色,可能导致数据意外暴露。 - 到处使用
select("*")— 这会获取所有列,包括敏感数据。只选择所需的列。 - Edge Functions 中使用已弃用的
serve导入 —import { serve } from "https://deno.land/std/http/server.ts"已弃用。请使用Deno.serve()。
常见错误:
- 在
.insert()或.update()后未添加.select()— 不使用.select()时,这些方法不会返回数据(仅返回null)。 - Edge Functions 中缺少 CORS 标头 — 缺少正确的 CORS 标头和 OPTIONS 处理时,浏览器请求会失败。
- 未取消订阅 Realtime 频道 — 这会泄漏 WebSocket 连接,并可能导致内存问题。
- Edge Functions 中使用裸模块说明符 —
import { createClient } from "@supabase/supabase-js"在 Deno 中会失败。请使用npm:@supabase/supabase-js@2。 - 使用
getSession()验证身份验证 —getSession()会从本地存储读取数据,可能遭到篡改。请使用getUser()进行安全的服务端验证。
注意事项与边缘情况:
- Realtime DELETE 事件无法筛选 — 无论筛选条件如何,都会收到订阅表的所有删除事件。
- Realtime 需要
replica identity full才能获取旧记录数据 — 默认情况下,UPDATE 和 DELETE 载荷仅包含新记录。设置alter table X replica identity full才能访问payload.old。 - RLS 策略不会应用于 Realtime DELETE 事件 — 请谨慎处理 DELETE 事件暴露的信息。
onAuthStateChange会在标签页获得焦点时触发 — 浏览器标签页重新获得焦点时会触发SIGNED_IN事件,而不仅仅是在实际登录时。- 切勿在
onAuthStateChange回调中调用 Supabase 方法 — 这可能导致死锁。请使用setTimeout(..., 0)延迟执行。 - 签名 URL 会过期 —
createSignedUrl()生成的 URL 会在指定时长后过期。签名上传 URL 会在 2 小时后过期。 - 公开存储桶 URL 会绕过 RLS — 公开存储桶中的文件任何人都可以通过 URL 访问,不受策略限制。
- Edge Function 冷启动 — 空闲一段时间后首次调用会有额外延迟。请设计“胖函数”(数量较少、体积较大的函数),以尽量减少冷启动。
- Edge Functions:文件只能写入
/tmp—/tmp目录是边缘函数中唯一可写入的路径。
</red_flags>
<critical_reminders>
关键提醒
所有代码都必须遵循 CLAUDE.md 中的项目约定(kebab-case、命名导出、导入顺序、
import type、命名常量)
(必须为暴露架构中的每张表启用行级安全性(RLS)——没有例外)
(必须在 createClient<Database>() 中使用 Database 泛型类型,以确保查询类型安全)
(绝不能在客户端代码中暴露密钥——在浏览器中使用可发布密钥,密钥仅限在服务器端使用)
(出于性能考虑,必须在 RLS 策略中将 (select auth.uid()) 包装在子查询中)
(必须使用 { data, error } 解构处理所有 Supabase 响应——绝不能假定操作成功)
不遵循这些规则会造成安全漏洞、类型不安全的查询以及无提示的运行时故障。
</critical_reminders>