mcpField
A
api-baas-supabase

api-baas-supabase

github.com/agents-inc开发2639 次安装2 天前更新
代码仓库

关于 api-baas-supabase

面向使用 Supabase 构建后端的开发者,整理其 Postgres、认证、实时订阅、文件存储和 Edge Functions 的开发模式。指南着重类型安全、RLS 数据访问策略与错误处理。

主要功能

  1. 01带 Database 泛型的类型安全客户端
  2. 02Postgres 查询与错误处理
  3. 03认证流程与会话管理
  4. 04RLS 策略与数据访问控制
  5. 05实时订阅、文件存储与 Edge Functions

适用场景

  1. 01使用 Supabase 客户端查询 Postgres
  2. 02实现 OAuth、魔法链接或会话管理
  3. 03为多用户数据配置 RLS 策略
  4. 04订阅数据库变更或上传文件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/agents-inc/skills --skill api-baas-supabase

安装后在 Claude Code 中输入/api-baas-supabase确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

metadata.yaml
reference.md
examples/
auth.md
core.md
database.md
edge-functions.md
storage.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 身份验证提供商(请改用专用身份验证技能)

详细资源:

客户端与查询:

身份验证:

  • examples/auth.md — 完整的身份验证流程、OAuth、魔法链接、会话刷新、中间件防护

数据库:

存储:

Edge Functions:


## 理念

Supabase 是一个基于 Postgres 构建的开源 Firebase 替代方案。它通过组合 Postgres 扩展、自动生成的 REST/GraphQL API、身份验证、实时订阅、文件存储和边缘函数,提供完整的后端。

核心原则:

  1. 以 Postgres 为核心 — 每项功能都基于 Postgres 构建。RLS 策略、身份验证和实时功能都利用了 Postgres 原语。理解 Postgres,就是理解 Supabase。
  2. 端到端类型安全 — 使用 supabase gen types 根据数据库架构生成 TypeScript 类型。将 Database 泛型传递给 createClient,即可让查询拥有完整类型。
  3. 默认安全 — 必须为每张表启用 RLS。可发布密钥可安全用于浏览器(RLS 会强制执行访问控制)。密钥会绕过 RLS,绝不能离开服务器。
  4. 错误作为值 — 每个 Supabase 方法都会返回 { data, error }。绝不要假定操作成功。使用 data 前始终检查 error。
  5. 内置实时功能 — Postgres 更改通过通道经由 WebSocket 推送。无需单独的发布/订阅基础设施。
  6. 边缘优先函数 — 边缘函数在靠近用户的边缘运行 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>

评论

支持 Markdown