supabase
Supabase 数据库开发规范、最佳实践、检查清单与 SQL 模板。
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Menu
Supabase 数据库开发规范、最佳实践、检查清单与 SQL 模板。
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Basé sur la classification professionnelle SOC
通用代码质量检查,包括语法错误、TypeScript 类型检查和 Prettier 代码美化。在提交代码前、完成功能开发后、或用户要求检查代码质量时使用。
Use when designing data structures, database schemas, state management, or data relationships - covers single source of truth, complete data flow thinking
开发环境部署到生产环境前的验证清单与最佳实践,重点关注 Monorepo 依赖管理与环境一致性。
Use when integrating with external APIs, third-party SDKs, or libraries - covers verification workflow before writing any integration code
Implements filterable, paginated list pages with URL as single source of truth (nuqs) and TanStack Query to avoid duplicate requests and support shareable links. Use when building list pages with filters, pagination, URL-synced state, or when fixing multiple requests on page change.
Use when working with Polymarket API - profiles, activities, events, markets endpoints
| name | supabase |
| description | Supabase 数据库开发规范、最佳实践、检查清单与 SQL 模板。 |
用于指导 Supabase 数据库开发的规范和最佳实践。
当进行以下操作时,请参考此规范:
# .env.example
# 本地开发环境
SUPABASE_URL=http://localhost:54321
SUPABASE_SERVICE_ROLE_KEY=your_local_service_role_key
# 生产环境
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_ROLE_KEY=your_production_service_role_key
# 正确做法:使用环境变量 + 单例模式
from supabase import Client, create_client
import os
_client: Client | None = None
def get_supabase_client() -> Client:
global _client
if _client is None:
url = os.getenv("SUPABASE_URL")
key = os.getenv("SUPABASE_SERVICE_ROLE_KEY")
if not url or not key:
raise ValueError("Supabase 环境变量未设置")
_client = create_client(url, key)
return _client
| Key 类型 | 用途 | RLS 行为 |
|---|---|---|
anon | 前端客户端 | 受 RLS 约束 |
service_role | 后端服务 | 绕过 RLS |
规则:
service_role keyservice_role keyanon key| 元素 | 规范 | 示例 |
|---|---|---|
| 表名 | snake_case,复数形式 | follow_rules, signals, orders |
| 列名 | snake_case | created_at, user_id, order_price |
| 主键 | id | id UUID PRIMARY KEY |
| 外键 | {关联表单数}_id | user_id, signal_id |
| 时间戳 | *_at 后缀 | created_at, updated_at, deleted_at |
每个表必须包含以下列:
CREATE TABLE table_name (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- ... 业务字段 ...
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
需要软删除的表添加 deleted_at 列:
deleted_at TIMESTAMPTZ -- NULL 表示未删除
查询时过滤已删除记录:
SELECT * FROM table_name WHERE deleted_at IS NULL;
必须为每个表创建 updated_at 触发器:
-- 通用触发器函数(只需创建一次)
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- 为每个表创建触发器
CREATE TRIGGER {table_name}_updated_at
BEFORE UPDATE ON {table_name}
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
| 数据类型 | PostgreSQL 类型 | 说明 |
|---|---|---|
| UUID | UUID | 主键、外键 |
| 时间 | TIMESTAMPTZ | 必须使用带时区的时间 |
| 金额/价格 | DECIMAL(18, 6) | 高精度数值 |
| 百分比/比例 | DECIMAL(10, 6) | 小数 |
| JSON 数据 | JSONB | 优先使用 JSONB |
| 布尔值 | BOOLEAN | 默认值明确设置 |
| 枚举值 | VARCHAR + CHECK | 使用约束验证 |
-- 推荐:使用 CHECK 约束
status VARCHAR(20) NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending', 'processing', 'completed', 'ignored'))
-- 不推荐:PostgreSQL ENUM 类型(修改困难)
-- 文本数组
leader_addresses TEXT[] NOT NULL
-- 查询示例
SELECT * FROM follow_rules WHERE 'address123' = ANY(leader_addresses);
所有表必须启用 RLS:
ALTER TABLE table_name ENABLE ROW LEVEL SECURITY;
格式:"{动作描述}"
-- 好的命名
"Users can view their own follow_rules"
"Users can insert signals for their follow_rules"
-- 避免的命名
"policy_1"
"select_policy"
-- 用户只能访问自己的数据
CREATE POLICY "Users can view their own {table}"
ON {table} FOR SELECT
USING (auth.uid() = user_id);
CREATE POLICY "Users can insert their own {table}"
ON {table} FOR INSERT
WITH CHECK (auth.uid() = user_id);
CREATE POLICY "Users can update their own {table}"
ON {table} FOR UPDATE
USING (auth.uid() = user_id);
CREATE POLICY "Users can delete their own {table}"
ON {table} FOR DELETE
USING (auth.uid() = user_id);
-- 通过父表关联验证权限
CREATE POLICY "Users can view {child_table} from their {parent_table}"
ON {child_table} FOR SELECT
USING (
EXISTS (
SELECT 1 FROM {parent_table}
WHERE {parent_table}.id = {child_table}.{parent}_id
AND {parent_table}.user_id = auth.uid()
)
);
-- 通过多级关联验证权限
CREATE POLICY "Users can view orders from their signals"
ON orders FOR SELECT
USING (
EXISTS (
SELECT 1 FROM signals
JOIN follow_rules ON follow_rules.id = signals.follow_rule_id
WHERE signals.id = orders.signal_id
AND follow_rules.user_id = auth.uid()
)
);
-- service_role key 自动绕过 RLS,无需额外配置
-- 如需在特定会话中绕过 RLS:
SET LOCAL request.jwt.claims.role = 'service_role';
-- 为 RLS 策略中使用的列创建索引
CREATE INDEX idx_{table}_user_id ON {table}(user_id);
-- 对于间接关联,确保外键有索引
CREATE INDEX idx_{child}_parent_id ON {child_table}({parent}_id);
每个表必须创建以下索引:
-- 1. 外键索引(加速 JOIN)
CREATE INDEX idx_{table}_{fk_column} ON {table}({fk_column});
-- 2. 常用查询条件索引
CREATE INDEX idx_{table}_{column} ON {table}({column});
-- 3. 时间范围查询索引(降序,便于获取最新数据)
CREATE INDEX idx_{table}_created_at ON {table}(created_at DESC);
格式:idx_{表名}_{列名}
-- 单列索引
CREATE INDEX idx_signals_status ON signals(status);
CREATE INDEX idx_signals_follow_rule_id ON signals(follow_rule_id);
-- 复合索引
CREATE INDEX idx_signals_status_created ON signals(status, created_at DESC);
| 场景 | 索引类型 | 示例 |
|---|---|---|
| 等值查询 | B-tree(默认) | WHERE status = 'pending' |
| 范围查询 | B-tree | WHERE created_at > '2024-01-01' |
| 全文搜索 | GIN | WHERE to_tsvector(content) @@ query |
| JSONB 查询 | GIN | WHERE config_json @> '{"key": "value"}' |
| 数组包含 | GIN | WHERE 'addr' = ANY(addresses) |
-- GIN 索引(支持 @>, ?, ?& 等操作符)
CREATE INDEX idx_{table}_{jsonb_col}_gin ON {table} USING GIN ({jsonb_col});
-- 特定路径索引
CREATE INDEX idx_{table}_{jsonb_path} ON {table} (({jsonb_col}->>'key'));
-- 只索引活跃数据
CREATE INDEX idx_follow_rules_active
ON follow_rules(user_id)
WHERE is_active = true AND deleted_at IS NULL;
migrations/
├── 001_init_schema.sql # 初始化 schema
├── 002_add_feature_x.sql # 添加功能 X
├── 003_optimize_indexes.sql # 优化索引
└── ...
格式:{序号}_{描述}.sql
001_init_schema.sql
002_add_user_preferences.sql
003_add_signals_market_title.sql
004_optimize_orders_indexes.sql
-- Migration: {序号}_{描述}
-- Description: {详细描述变更内容}
-- Author: {作者}
-- Date: {日期}
-- ============================================
-- 1. 变更描述
-- ============================================
-- 添加新列
ALTER TABLE {table} ADD COLUMN {column} {type};
-- 添加索引
CREATE INDEX IF NOT EXISTS idx_{table}_{column} ON {table}({column});
-- 添加约束
ALTER TABLE {table} ADD CONSTRAINT {constraint_name} CHECK (...);
-- ============================================
-- 2. RLS 策略更新(如需要)
-- ============================================
-- ...
-- ============================================
-- 3. 回滚脚本(注释形式保留)
-- ============================================
-- ROLLBACK:
-- ALTER TABLE {table} DROP COLUMN {column};
-- DROP INDEX idx_{table}_{column};
使用 IF NOT EXISTS / IF EXISTS
CREATE TABLE IF NOT EXISTS ...
CREATE INDEX IF NOT EXISTS ...
DROP TABLE IF EXISTS ...
小步迭代:每个迁移文件只做一件事
向后兼容:新增列提供默认值或允许 NULL
先添加后删除:
-- 步骤 1:添加新列
ALTER TABLE users ADD COLUMN new_email VARCHAR(255);
-- 步骤 2:迁移数据(另一个迁移文件)
UPDATE users SET new_email = email;
-- 步骤 3:删除旧列(确认无问题后)
ALTER TABLE users DROP COLUMN email;
保留回滚脚本:在注释中记录如何回滚
# 本地环境:通过 Supabase CLI
supabase db reset # 重置并应用所有迁移
supabase db push # 推送迁移到远程
# 生产环境:通过 Supabase Dashboard 或 CLI
supabase db push --db-url $PROD_URL # 推送到生产数据库
| 配置项 | 本地开发 | 生产环境 |
|---|---|---|
| SUPABASE_URL | http://localhost:54321 | https://{project}.supabase.co |
| Database | 本地 PostgreSQL | Supabase 托管 |
| 数据 | 测试数据 | 真实数据 |
| RLS | 可禁用测试 | 必须启用 |
# macOS
brew install supabase/tap/supabase
# npm
npm install -g supabase
supabase init
supabase start
# .env.local
SUPABASE_URL=http://localhost:54321
SUPABASE_SERVICE_ROLE_KEY=<local-service-role-key>
SUPABASE_ANON_KEY=<local-anon-key>
获取本地 keys:
supabase status
# 重置数据库并应用迁移
supabase db reset
# 生成迁移文件(从本地 schema 变更)
supabase db diff -f {migration_name}
# 查看本地数据库
supabase db studio # 打开本地 Studio
service_role key 仅在后端anon key 仅在前端使用# .env.production
SUPABASE_URL=https://{project}.supabase.co
SUPABASE_SERVICE_ROLE_KEY=<production-service-role-key>
# 方式一:Supabase CLI
supabase link --project-ref {project-id}
supabase db push
# 方式二:Dashboard SQL Editor
# 1. 登录 Supabase Dashboard
# 2. 进入 SQL Editor
# 3. 粘贴迁移脚本执行
# 方式三:直接连接(需谨慎)
psql $DATABASE_URL -f migrations/xxx.sql
# 生产环境必须使用 SSL
import os
from supabase import create_client
client = create_client(
os.getenv("SUPABASE_URL"),
os.getenv("SUPABASE_SERVICE_ROLE_KEY")
)
import os
def is_local_env() -> bool:
"""判断是否为本地开发环境"""
url = os.getenv("SUPABASE_URL", "")
return "localhost" in url or "127.0.0.1" in url
def get_db_config():
if is_local_env():
return {
"ssl": False,
"timeout": 30,
}
else:
return {
"ssl": True,
"timeout": 10,
}
# 导出特定表(脱敏后)
pg_dump $PROD_URL -t follow_rules --data-only > seed_data.sql
# 导入到本地
psql $LOCAL_URL < seed_data.sql
-- supabase/seed.sql
-- 本地开发测试数据
INSERT INTO follow_rules (id, user_id, name, leader_addresses, config_version, config_json)
VALUES (
'00000000-0000-0000-0000-000000000001',
'auth-user-uuid',
'Test Rule',
ARRAY['0x123', '0x456'],
1,
'{"fixed_slippage": 0.01, "min_usdc_size": 10, "cooldown_seconds": 60}'
);
[ ] 表名:snake_case 复数形式(如 follow_rules, signals)
[ ] 主键:id UUID PRIMARY KEY DEFAULT gen_random_uuid()
[ ] 时间戳:created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
[ ] 时间戳:updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
[ ] 触发器:创建 {table}_updated_at 触发器
[ ] RLS:ALTER TABLE {table} ENABLE ROW LEVEL SECURITY
[ ] 策略:创建 SELECT/INSERT/UPDATE/DELETE 策略
[ ] 索引:为外键列创建索引
[ ] 索引:为常用查询条件创建索引
[ ] 索引:为 created_at 创建降序索引
[ ] 所有表已启用 RLS
[ ] 直接关联表:使用 auth.uid() = user_id
[ ] 间接关联表:使用 EXISTS 子查询验证权限链
[ ] 后端服务:确认使用 service_role key
[ ] 前端:确认只使用 anon key
[ ] 性能:RLS 策略使用的列有索引
[ ] 文件名:{序号}_{描述}.sql(如 001_init_schema.sql)
[ ] 头部注释:包含 Migration、Description、Author、Date
[ ] 幂等性:使用 IF NOT EXISTS / IF EXISTS
[ ] 回滚脚本:在注释中保留回滚 SQL
[ ] 小步迭代:每个迁移只做一件事
[ ] 向后兼容:新列有默认值或允许 NULL
[ ] 本地测试:在本地环境验证迁移
[ ] RLS 验证:确认 RLS 策略正确
[ ] 索引验证:确认必要索引存在
[ ] 环境变量:生产环境变量已配置
[ ] 备份:重大变更前已备份数据库
[ ] 回滚准备:准备好回滚脚本
[ ] Supabase CLI 已安装
[ ] supabase init 已执行
[ ] supabase start 已启动
[ ] .env.local 已配置本地 URL 和 Key
[ ] .env.production 已配置生产 URL 和 Key
[ ] service_role key 仅在后端使用
[ ] SSL 连接已启用
[ ] 敏感数据已脱敏处理
ALTER TABLE xxx ENABLE ROW LEVEL SECURITYSELECT * FROM pg_policies WHERE tablename = 'xxx'