supabase

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Supabase Core Skill

Supabase核心技能

Load with: base.md + [supabase-nextjs.md | supabase-python.md | supabase-node.md]
Core concepts, CLI workflow, and patterns common to all Supabase projects.

加载方式:base.md + [supabase-nextjs.md | supabase-python.md | supabase-node.md]
所有Supabase项目通用的核心概念、CLI工作流和模式。
资料来源: Supabase文档 | Supabase CLI

Core Principle

核心原则

Local-first, migrations in version control, never touch production directly.
Develop locally with the Supabase CLI, capture all changes as migrations, and deploy through CI/CD.

优先本地开发,迁移纳入版本控制,绝不直接修改生产环境。
使用Supabase CLI在本地开发,将所有变更捕获为迁移,并通过CI/CD部署。

Supabase Stack

Supabase技术栈

ServicePurpose
DatabasePostgreSQL with extensions
AuthUser authentication, OAuth providers
StorageFile storage with RLS
Edge FunctionsServerless Deno functions
RealtimeWebSocket subscriptions
VectorAI embeddings (pgvector)

服务用途
Database带扩展的PostgreSQL
Auth用户认证、OAuth提供商
Storage带RLS的文件存储
Edge Functions无服务器Deno函数
RealtimeWebSocket订阅
VectorAI嵌入(pgvector)

CLI Setup

CLI设置

Install & Login

安装与登录

bash
undefined
bash
undefined

macOS

macOS

brew install supabase/tap/supabase
brew install supabase/tap/supabase

npm (alternative)

npm (替代方案)

npm install -g supabase
npm install -g supabase

Login

登录

supabase login
undefined
supabase login
undefined

Initialize Project

初始化项目

bash
undefined
bash
undefined

In your project directory

在你的项目目录中

supabase init
supabase init

Creates:

创建以下文件:

supabase/

supabase/

├── config.toml # Local config

├── config.toml # 本地配置

├── seed.sql # Seed data

├── seed.sql # 种子数据

└── migrations/ # SQL migrations

└── migrations/ # SQL迁移文件

undefined
undefined

Link to Remote

关联远程项目

bash
undefined
bash
undefined

Get project ref from dashboard URL: https://supabase.com/dashboard/project/<ref>

从控制台URL获取项目引用:https://supabase.com/dashboard/project/<ref>

supabase link --project-ref <project-id>
supabase link --project-ref <project-id>

Pull existing schema

拉取现有架构

supabase db pull
undefined
supabase db pull
undefined

Start Local Stack

启动本地服务栈

bash
supabase start
bash
supabase start

Output:

输出:

DB URL: postgresql://postgres:postgres@localhost:54322/postgres

DB URL: postgresql://postgres:postgres@localhost:54322/postgres

Anon key: eyJ...

Anon key: eyJ...

Service role key: eyJ...

Service role key: eyJ...


---

---

Migration Workflow

迁移工作流

Option 1: Dashboard + Diff (Quick Prototyping)

选项1:控制台+差异对比(快速原型开发)

bash
undefined
bash
undefined

1. Make changes in local Studio (localhost:54323)

1. 在本地Studio(localhost:54323)中进行变更

2. Generate migration from diff

2. 通过差异对比生成迁移文件

supabase db diff -f <migration_name>
supabase db diff -f <migration_name>

3. Review generated SQL

3. 查看生成的SQL

cat supabase/migrations/*_<migration_name>.sql
cat supabase/migrations/*_<migration_name>.sql

4. Reset to test

4. 重置数据库进行测试

supabase db reset
undefined
supabase db reset
undefined

Option 2: Write Migrations Directly (Recommended)

选项2:直接编写迁移文件(推荐)

bash
undefined
bash
undefined

1. Create empty migration

1. 创建空的迁移文件

supabase migration new create_users_table
supabase migration new create_users_table

2. Edit the migration file

2. 编辑迁移文件

supabase/migrations/<timestamp>_create_users_table.sql

supabase/migrations/<timestamp>_create_users_table.sql

3. Apply locally

3. 在本地应用迁移

supabase db reset
undefined
supabase db reset
undefined

Option 3: ORM Migrations (Best DX)

选项3:ORM迁移(最佳开发体验)

Use Drizzle (TypeScript) or SQLAlchemy (Python) - see framework-specific skills.
使用Drizzle(TypeScript)或SQLAlchemy(Python)——详见框架专属技能文档。

Deploy Migrations

部署迁移

bash
undefined
bash
undefined

Push to remote (staging/production)

推送到远程环境(预发布/生产)

supabase db push
supabase db push

Check migration status

查看迁移状态

supabase migration list

---
supabase migration list

---

Database Patterns

数据库模式

Enable RLS on All Tables

为所有表启用RLS

sql
-- Always enable RLS
ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;

-- Default deny - must create policies
CREATE POLICY "Users can view own profile"
  ON public.profiles
  FOR SELECT
  USING (auth.uid() = id);
sql
-- 始终启用RLS
ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;

-- 默认拒绝访问 - 必须创建策略
CREATE POLICY "Users can view own profile"
  ON public.profiles
  FOR SELECT
  USING (auth.uid() = id);

Common RLS Policies

常见RLS策略

sql
-- Public read
CREATE POLICY "Public read access"
  ON public.posts FOR SELECT
  USING (true);

-- Authenticated users only
CREATE POLICY "Authenticated users can insert"
  ON public.posts FOR INSERT
  WITH CHECK (auth.role() = 'authenticated');

-- Owner access
CREATE POLICY "Users can update own records"
  ON public.posts FOR UPDATE
  USING (auth.uid() = user_id);

-- Admin access (using custom claim)
CREATE POLICY "Admins have full access"
  ON public.posts FOR ALL
  USING (auth.jwt() ->> 'role' = 'admin');
sql
-- 公开读取
CREATE POLICY "Public read access"
  ON public.posts FOR SELECT
  USING (true);

-- 仅允许已认证用户
CREATE POLICY "Authenticated users can insert"
  ON public.posts FOR INSERT
  WITH CHECK (auth.role() = 'authenticated');

-- 所有者访问
CREATE POLICY "Users can update own records"
  ON public.posts FOR UPDATE
  USING (auth.uid() = user_id);

-- 管理员访问(使用自定义声明)
CREATE POLICY "Admins have full access"
  ON public.posts FOR ALL
  USING (auth.jwt() ->> 'role' = 'admin');

Link to auth.users

关联auth.users表

sql
-- Profile table linked to auth
CREATE TABLE public.profiles (
  id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
  username TEXT UNIQUE NOT NULL,
  avatar_url TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Auto-create profile on signup
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS TRIGGER AS $$
BEGIN
  INSERT INTO public.profiles (id, username)
  VALUES (NEW.id, NEW.email);
  RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

CREATE TRIGGER on_auth_user_created
  AFTER INSERT ON auth.users
  FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();

sql
-- 关联到认证系统的用户资料表
CREATE TABLE public.profiles (
  id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
  username TEXT UNIQUE NOT NULL,
  avatar_url TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- 用户注册时自动创建资料
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS TRIGGER AS $$
BEGIN
  INSERT INTO public.profiles (id, username)
  VALUES (NEW.id, NEW.email);
  RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

CREATE TRIGGER on_auth_user_created
  AFTER INSERT ON auth.users
  FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();

Seed Data

种子数据

supabase/seed.sql

supabase/seed.sql

sql
-- Runs on `supabase db reset`
-- Use ON CONFLICT for idempotency

INSERT INTO public.profiles (id, username, avatar_url)
VALUES
  ('d0e1f2a3-b4c5-6d7e-8f9a-0b1c2d3e4f5a', 'testuser', null),
  ('a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d', 'admin', null)
ON CONFLICT (id) DO NOTHING;

sql
-- 在`supabase db reset`时执行
-- 使用ON CONFLICT保证幂等性

INSERT INTO public.profiles (id, username, avatar_url)
VALUES
  ('d0e1f2a3-b4c5-6d7e-8f9a-0b1c2d3e4f5a', 'testuser', null),
  ('a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d', 'admin', null)
ON CONFLICT (id) DO NOTHING;

Environment Variables

环境变量

Required Variables

必填变量

bash
undefined
bash
undefined

Public (safe for client-side)

公开变量(可安全用于客户端)

SUPABASE_URL=https://xxxxx.supabase.co SUPABASE_ANON_KEY=eyJ...
SUPABASE_URL=https://xxxxx.supabase.co SUPABASE_ANON_KEY=eyJ...

Private (server-side only - NEVER expose)

私有变量(仅用于服务端 - 绝不可暴露)

SUPABASE_SERVICE_ROLE_KEY=eyJ... DATABASE_URL=postgresql://postgres.[ref]:[password]@aws-0-region.pooler.supabase.com:6543/postgres
undefined
SUPABASE_SERVICE_ROLE_KEY=eyJ... DATABASE_URL=postgresql://postgres.[ref]:[password]@aws-0-region.pooler.supabase.com:6543/postgres
undefined

Local vs Production

本地与生产环境对比

bash
undefined
bash
undefined

.env.local (local development)

.env.local(本地开发)

SUPABASE_URL=http://localhost:54321 SUPABASE_ANON_KEY=<from supabase start> DATABASE_URL=postgresql://postgres:postgres@localhost:54322/postgres
SUPABASE_URL=http://localhost:54321 SUPABASE_ANON_KEY=<来自supabase start的输出> DATABASE_URL=postgresql://postgres:postgres@localhost:54322/postgres

.env.production (remote)

.env.production(远程环境)

SUPABASE_URL=https://xxxxx.supabase.co SUPABASE_ANON_KEY=<from dashboard> DATABASE_URL=<connection pooler URL>
undefined
SUPABASE_URL=https://xxxxx.supabase.co SUPABASE_ANON_KEY=<来自控制台> DATABASE_URL=<连接池URL>
undefined

Connection Pooling

连接池

bash
undefined
bash
undefined

Transaction mode (recommended for serverless)

事务模式(推荐用于无服务器环境)

Add ?pgbouncer=true to URL

在URL后添加?pgbouncer=true

DATABASE_URL=postgresql://...@pooler.supabase.com:6543/postgres?pgbouncer=true
DATABASE_URL=postgresql://...@pooler.supabase.com:6543/postgres?pgbouncer=true

Session mode (for migrations, long transactions)

会话模式(用于迁移、长事务)

DATABASE_URL=postgresql://...@pooler.supabase.com:5432/postgres

---
DATABASE_URL=postgresql://...@pooler.supabase.com:5432/postgres

---

Edge Functions

Edge Functions

Create Function

创建函数

bash
supabase functions new hello-world
bash
supabase functions new hello-world

Basic Structure

基础结构

typescript
// supabase/functions/hello-world/index.ts
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';

serve(async (req) => {
  const { name } = await req.json();

  return new Response(
    JSON.stringify({ message: `Hello ${name}!` }),
    { headers: { 'Content-Type': 'application/json' } }
  );
});
typescript
// supabase/functions/hello-world/index.ts
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';

serve(async (req) => {
  const { name } = await req.json();

  return new Response(
    JSON.stringify({ message: `Hello ${name}!` }),
    { headers: { 'Content-Type': 'application/json' } }
  );
});

With Auth Context

带认证上下文

typescript
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';

serve(async (req) => {
  const supabase = createClient(
    Deno.env.get('SUPABASE_URL') ?? '',
    Deno.env.get('SUPABASE_ANON_KEY') ?? '',
    {
      global: {
        headers: { Authorization: req.headers.get('Authorization')! },
      },
    }
  );

  const { data: { user } } = await supabase.auth.getUser();

  if (!user) {
    return new Response('Unauthorized', { status: 401 });
  }

  return new Response(JSON.stringify({ user_id: user.id }));
});
typescript
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';

serve(async (req) => {
  const supabase = createClient(
    Deno.env.get('SUPABASE_URL') ?? '',
    Deno.env.get('SUPABASE_ANON_KEY') ?? '',
    {
      global: {
        headers: { Authorization: req.headers.get('Authorization')! },
      },
    }
  );

  const { data: { user } } = await supabase.auth.getUser();

  if (!user) {
    return new Response('Unauthorized', { status: 401 });
  }

  return new Response(JSON.stringify({ user_id: user.id }));
});

Deploy

部署

bash
undefined
bash
undefined

Serve locally

本地运行

supabase functions serve
supabase functions serve

Deploy single function

部署单个函数

supabase functions deploy hello-world
supabase functions deploy hello-world

Deploy all

部署所有函数

supabase functions deploy

---
supabase functions deploy

---

Storage

存储

Create Bucket (in migration)

创建存储桶(在迁移文件中)

sql
INSERT INTO storage.buckets (id, name, public)
VALUES ('avatars', 'avatars', true);

-- Storage policies
CREATE POLICY "Avatar images are publicly accessible"
  ON storage.objects FOR SELECT
  USING (bucket_id = 'avatars');

CREATE POLICY "Users can upload own avatar"
  ON storage.objects FOR INSERT
  WITH CHECK (
    bucket_id = 'avatars' AND
    auth.uid()::text = (storage.foldername(name))[1]
  );

sql
INSERT INTO storage.buckets (id, name, public)
VALUES ('avatars', 'avatars', true);

-- 存储策略
CREATE POLICY "Avatar images are publicly accessible"
  ON storage.objects FOR SELECT
  USING (bucket_id = 'avatars');

CREATE POLICY "Users can upload own avatar"
  ON storage.objects FOR INSERT
  WITH CHECK (
    bucket_id = 'avatars' AND
    auth.uid()::text = (storage.foldername(name))[1]
  );

CLI Quick Reference

CLI速查手册

bash
undefined
bash
undefined

Lifecycle

生命周期

supabase start # Start local stack supabase stop # Stop local stack supabase status # Show status & credentials
supabase start # 启动本地服务栈 supabase stop # 停止本地服务栈 supabase status # 显示状态与凭证

Database

数据库

supabase db reset # Reset + migrations + seed supabase db push # Push to remote supabase db pull # Pull remote schema supabase db diff -f <name> # Generate migration from diff supabase db lint # Check for issues
supabase db reset # 重置数据库 + 执行迁移 + 种子数据 supabase db push # 推送到远程环境 supabase db pull # 拉取远程架构 supabase db diff -f <name> # 通过差异对比生成迁移文件 supabase db lint # 检查问题

Migrations

迁移

supabase migration new <name> # Create migration supabase migration list # List migrations supabase migration up # Apply pending (remote)
supabase migration new <name> # 创建迁移文件 supabase migration list # 列出迁移文件 supabase migration up # 应用待处理迁移(远程环境)

Functions

函数

supabase functions new <name> # Create function supabase functions serve # Local dev supabase functions deploy # Deploy all
supabase functions new <name> # 创建函数 supabase functions serve # 本地开发 supabase functions deploy # 部署所有函数

Types

类型生成

supabase gen types typescript --local > types/database.ts
supabase gen types typescript --local > types/database.ts

Project

项目管理

supabase link --project-ref <id> # Link to remote supabase projects list # List projects

---
supabase link --project-ref <id> # 关联到远程项目 supabase projects list # 列出项目

---

CI/CD Template

CI/CD模板

yaml
undefined
yaml
undefined

.github/workflows/supabase.yml

.github/workflows/supabase.yml

name: Supabase CI/CD
on: push: branches: [main] pull_request:
env: SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }} SUPABASE_DB_PASSWORD: ${{ secrets.SUPABASE_DB_PASSWORD }} SUPABASE_PROJECT_ID: ${{ secrets.SUPABASE_PROJECT_ID }}
jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: supabase/setup-cli@v1
  - name: Start Supabase
    run: supabase start

  - name: Run migrations
    run: supabase db reset

  - name: Lint database
    run: supabase db lint
deploy: needs: test if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: supabase/setup-cli@v1
  - name: Link project
    run: supabase link --project-ref $SUPABASE_PROJECT_ID

  - name: Push migrations
    run: supabase db push

  - name: Deploy functions
    run: supabase functions deploy

---
name: Supabase CI/CD
on: push: branches: [main] pull_request:
env: SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }} SUPABASE_DB_PASSWORD: ${{ secrets.SUPABASE_DB_PASSWORD }} SUPABASE_PROJECT_ID: ${{ secrets.SUPABASE_PROJECT_ID }}
jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: supabase/setup-cli@v1
  - name: Start Supabase
    run: supabase start

  - name: Run migrations
    run: supabase db reset

  - name: Lint database
    run: supabase db lint
deploy: needs: test if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: supabase/setup-cli@v1
  - name: Link project
    run: supabase link --project-ref $SUPABASE_PROJECT_ID

  - name: Push migrations
    run: supabase db push

  - name: Deploy functions
    run: supabase functions deploy

---

Anti-Patterns

反模式

  • Direct production changes - Always use migrations
  • Disabled RLS - Enable on all user-data tables
  • Service key in client - Never expose service role key
  • No connection pooling - Use pooler for serverless
  • Committing .env - Add to .gitignore
  • Skipping migration review - Always check generated SQL
  • No seed data - Use seed.sql for consistent local dev
  • 直接修改生产环境 - 始终使用迁移文件
  • 禁用RLS - 为所有用户数据表启用RLS
  • 客户端使用服务端密钥 - 绝不可暴露服务端角色密钥
  • 未使用连接池 - 为无服务器环境使用连接池
  • 提交.env文件 - 将其添加到.gitignore
  • 跳过迁移文件审查 - 始终检查生成的SQL
  • 无种子数据 - 使用seed.sql保证本地开发一致性