SupabasePatternsSkill supabase-patterns

提供适用于所有项目的Supabase最佳实践,包括行级安全、实时订阅、存储和边缘函数。

后端开发 0 次安装 0 次浏览 更新于 3/3/2026

name: supabase-patterns description: “适用于所有项目的Supabase最佳实践,包括行级安全、实时订阅、存储和边缘函数。不依赖于特定框架。”

Supabase Patterns 技能

适用于任何项目的通用Supabase模式。涵盖RLS策略、实时性、存储、边缘函数和迁移。

设计原则

这项技能是框架通用的。它提供通用的Supabase模式:

  • 不针对Book-Vetting、ocr-service或任何特定项目定制
  • 涵盖适用于所有Supabase项目通用模式
  • 项目特定的配置放在项目特定的技能中

变量

变量 默认值 描述
SUPABASE_DIR supabase Supabase配置目录
ENFORCE_RLS true 要求所有表都使用RLS
REALTIME_ENABLED auto 自动检测实时表

指南

强制性 - 按照以下工作流程顺序进行。

  1. 检查Supabase项目配置
  2. 查看现有的RLS策略
  3. 遵循安全第一的模式
  4. 保持迁移有序

红旗 - 停止并重新考虑

如果你即将:

  • 无RLS策略创建表
  • 在客户端代码中使用服务角色密钥
  • 跳过迁移进行架构更改
  • 在实时中暴露敏感数据

停止 -> 添加RLS策略 -> 使用适当的密钥 -> 然后继续

食谱书

RLS策略

  • 如果:创建或修改RLS策略
  • 那么:阅读并执行./cookbook/rls-policies.md

实时订阅

  • 如果:设置实时功能
  • 那么:阅读并执行./cookbook/realtime-subscriptions.md

存储模式

  • 如果:使用Supabase存储
  • 那么:阅读并执行./cookbook/storage-patterns.md

快速参考

项目结构

supabase/
├── config.toml           # 项目配置
├── migrations/           # SQL迁移
│   ├── 20231201000000_initial.sql
│   └── 20231202000000_add_users.sql
├── seed.sql             # 种子数据
└── functions/           # 边缘函数
    └── hello/
        └── index.ts

关键命令

# 初始化项目
supabase init

# 开始本地开发
supabase start

# 生成迁移
supabase migration new my_migration

# 推送到远程
supabase db push

# 生成类型
supabase gen types typescript --local > types/supabase.ts

RLS策略模式

-- 启用RLS
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;

-- 用户拥有行
CREATE POLICY "Users can view own posts"
  ON posts FOR SELECT
  USING (auth.uid() = user_id);

-- 用户可以插入自己的
CREATE POLICY "Users can create posts"
  ON posts FOR INSERT
  WITH CHECK (auth.uid() = user_id);

-- 公共读取
CREATE POLICY "Public read"
  ON posts FOR SELECT
  USING (is_public = true);

客户端模式

// 初始化客户端
import { createClient } from '@supabase/supabase-js';
import type { Database } from './types/supabase';

const supabase = createClient<Database>(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_ANON_KEY!
);

// 使用类型查询
const { data, error } = await supabase
  .from('posts')
  .select('*')
  .eq('user_id', userId);

// 插入
const { data, error } = await supabase
  .from('posts')
  .insert({ title, content, user_id: userId })
  .select()
  .single();

实时模式

// 订阅更改
const subscription = supabase
  .channel('posts')
  .on(
    'postgres_changes',
    { event: '*', schema: 'public', table: 'posts' },
    (payload) => {
      console.log('Change:', payload);
    }
  )
  .subscribe();

// 清理
subscription.unsubscribe();

存储模式

// 上传文件
const { data, error } = await supabase.storage
  .from('avatars')
  .upload(`${userId}/avatar.png`, file, {
    upsert: true,
    contentType: 'image/png'
  });

// 获取公共URL
const { data: { publicUrl } } = supabase.storage
  .from('avatars')
  .getPublicUrl(`${userId}/avatar.png`);

安全清单

生产前

  • [ ] 所有表都启用了RLS
  • [ ] 服务角色密钥不在客户端代码中
  • [ ] 仅用于公共操作的匿名密钥
  • [ ] 存储桶有策略
  • [ ] 从实时中排除敏感列
  • [ ] 配置了API速率限制
  • [ ] 正确配置了CORS

RLS清单

  • [ ] 每个表都启用了RLS
  • [ ] 定义了SELECT策略
  • [ ] 定义了INSERT/UPDATE/DELETE策略
  • [ ] 使用不同角色测试了策略
  • [ ] 没有过于宽松的策略

集成

与架构对齐

Supabase迁移应与ORM模型对齐:

-- supabase/migrations/20231201000000_users.sql
CREATE TABLE users (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  email TEXT UNIQUE NOT NULL,
  name TEXT,
  created_at TIMESTAMPTZ DEFAULT now()
);

应匹配:

# SQLAlchemy模型
class User(Base):
    id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=uuid.uuid4)
    email: Mapped[str] = mapped_column(unique=True)
    name: Mapped[str | None]
    created_at: Mapped[datetime] = mapped_column(server_default=func.now())

类型生成

# 从本地架构生成TypeScript类型
supabase gen types typescript --local > types/supabase.ts

# 在客户端使用
import type { Database } from './types/supabase';
type Post = Database['public']['Tables']['posts']['Row'];

最佳实践

  1. RLS优先:创建表时始终添加RLS策略
  2. 迁移一切:永远不要直接修改架构
  3. 类型安全:生成并使用TypeScript类型
  4. 密钥卫生:客户端使用匿名密钥,服务密钥仅在服务器端使用
  5. 测试策略:使用实际用户上下文测试RLS
  6. 谨慎使用实时:仅对需要的表启用