跳转至

Supabase — 开源后端即服务

一句话说明: Supabase 是 Firebase 的开源替代方案,提供 PostgreSQL 数据库、身份认证、文件存储、实时订阅和 Edge Functions 的一站式后端平台。


为什么要学

  1. 面试加分 — 展示你理解 BaaS 架构、Serverless 模式和全栈快速交付能力
  2. 实用性 — 免费额度足够个人项目,从原型到生产无缝扩展
  3. 开源 — 可自托管,避免厂商锁定;基于 PostgreSQL,SQL 技能通用
  4. 全栈整合 — 前端直连数据库(RLS 安全),无需写传统后端 CRUD

核心概念详解

Supabase vs Firebase

白话: Firebase 是 Google 的闭源 BaaS,用 NoSQL。Supabase 是开源版本,用 PostgreSQL(关系型),对开发者更友好。

对比项FirebaseSupabase
数据库Firestore (NoSQL)PostgreSQL (SQL)
开源
自托管不支持支持
实时原生Realtime + Postgres Changes
AuthFirebase AuthGoTrue (兼容 OAuth)
存储Cloud StorageS3 兼容
FunctionsCloud FunctionsEdge Functions (Deno)
定价按读写次数按资源用量

Row Level Security (RLS)

白话: 数据库级别的权限控制——不用写后端代码,直接在 SQL 里定义"谁能看/改哪些行"。前端可以安全地直连数据库。

-- 用户只能看自己的数据
CREATE POLICY "Users can view own data"
ON todos FOR SELECT
USING (auth.uid() = user_id);

PostgREST

白话: 自动把 PostgreSQL 表变成 REST API。建好表就有 CRUD 接口,零代码。

Realtime

白话: 数据库有变化时,前端自动收到通知(WebSocket)。适合聊天、协作编辑、实时仪表盘。


安装与配置

云端使用(最简单)

  1. 访问 https://supabase.com → 注册
  2. 创建新项目(选区域、设密码)
  3. 获得项目 URL + anon key

本地开发

# 安装 Supabase CLI
npm install -g supabase

# 或用 Homebrew
brew install supabase/tap/supabase

# 初始化项目
supabase init

# 启动本地 Supabase(需要 Docker)
supabase start

# 输出本地连接信息
# API URL: http://localhost:54321
# anon key: eyJhbGci...
# DB URL: postgresql://postgres:postgres@localhost:54322/postgres

前端 SDK 安装

# JavaScript / TypeScript
npm install @supabase/supabase-js

# Python
pip install supabase

快速上手

5 分钟入门(JavaScript)

import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  'https://xxx.supabase.co',  // 项目 URL
  'eyJhbGci...'                // anon key
)

// 查询数据
const { data, error } = await supabase
  .from('todos')
  .select('*')
  .eq('completed', false)

// 插入数据
const { data, error } = await supabase
  .from('todos')
  .insert({ title: '学习 Supabase', user_id: userId })

// 用户注册
const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'secure-password'
})

// 用户登录
const { data, error } = await supabase.auth.signInWithPassword({
  email: 'user@example.com',
  password: 'secure-password'
})

5 分钟入门(Python)

from supabase import create_client

supabase = create_client(
    "https://xxx.supabase.co",
    "eyJhbGci..."
)

# 查询
response = supabase.table("todos").select("*").eq("completed", False).execute()
print(response.data)

# 插入
response = supabase.table("todos").insert({
    "title": "学习 Supabase",
    "user_id": "uuid-here"
}).execute()

进阶用法

1. 数据库设计 + RLS

-- 创建表
CREATE TABLE todos (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  user_id UUID REFERENCES auth.users(id) NOT NULL,
  title TEXT NOT NULL,
  completed BOOLEAN DEFAULT false,
  created_at TIMESTAMPTZ DEFAULT now()
);

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

-- 策略:用户只能操作自己的数据
CREATE POLICY "Users manage own todos" ON todos
  FOR ALL USING (auth.uid() = user_id);

-- 策略:允许插入(验证 user_id 匹配)
CREATE POLICY "Users insert own todos" ON todos
  FOR INSERT WITH CHECK (auth.uid() = user_id);

2. 实时订阅

// 监听 todos 表变化
const channel = supabase
  .channel('todos-changes')
  .on(
    'postgres_changes',
    { event: '*', schema: 'public', table: 'todos' },
    (payload) => {
      console.log('Change:', payload.eventType, payload.new)
    }
  )
  .subscribe()

// 取消订阅
channel.unsubscribe()

3. 文件存储

// 上传文件
const { data, error } = await supabase.storage
  .from('avatars')
  .upload('user123/avatar.png', file)

// 获取公开 URL
const { data } = supabase.storage
  .from('avatars')
  .getPublicUrl('user123/avatar.png')

// 下载
const { data, error } = await supabase.storage
  .from('avatars')
  .download('user123/avatar.png')

4. Edge Functions (Deno)

# 创建函数
supabase functions new hello-world
// 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" } }
  )
})
# 部署
supabase functions deploy hello-world

# 本地测试
supabase functions serve hello-world

5. Auth 高级配置

// OAuth 登录(GitHub)
const { data, error } = await supabase.auth.signInWithOAuth({
  provider: 'github',
  options: {
    redirectTo: 'http://localhost:3000/callback'
  }
})

// Magic Link(邮件链接登录)
const { data, error } = await supabase.auth.signInWithOtp({
  email: 'user@example.com'
})

// 获取当前用户
const { data: { user } } = await supabase.auth.getUser()

6. 数据库函数 + RPC

-- 创建数据库函数
CREATE OR REPLACE FUNCTION get_user_stats(uid UUID)
RETURNS JSON AS $$
  SELECT json_build_object(
    'total_todos', COUNT(*),
    'completed', COUNT(*) FILTER (WHERE completed),
    'pending', COUNT(*) FILTER (WHERE NOT completed)
  )
  FROM todos WHERE user_id = uid;
$$ LANGUAGE SQL SECURITY DEFINER;
// 前端调用
const { data, error } = await supabase.rpc('get_user_stats', {
  uid: userId
})

7. 数据库迁移

# 创建迁移
supabase migration new add_priority_column

# 编辑迁移文件
# supabase/migrations/20240101000000_add_priority_column.sql

# 应用迁移
supabase db push

# 重置数据库
supabase db reset

常见问题与排错

问题原因解决方案
查询返回空数组RLS 未配置/策略不匹配检查 RLS 策略,临时禁用测试
permission deniedRLS 阻止了操作添加对应操作的 policy
实时不触发表未开启 ReplicationDashboard → Database → Replication → 启用表
Auth token 过期默认 1 小时客户端自动刷新,检查 session 管理
Edge Function 冷启动首次调用需初始化使用 warm-up 请求或接受延迟

面试高频考点

  1. RLS 为什么重要?
  2. 前端直连数据库时,RLS 是安全屏障;无需中间后端层做权限检查

  3. Supabase 的架构组成?

  4. PostgreSQL + PostgREST + GoTrue(Auth) + Realtime + Storage + Edge Functions

  5. 什么时候不该用 BaaS?

  6. 复杂业务逻辑、需要事务一致性、已有成熟后端团队

  7. 实时功能的实现原理?

  8. PostgreSQL logical replication → Realtime server → WebSocket 推送

  9. 自托管 vs 云服务的取舍?

  10. 云:零运维、自动备份;自托管:完全控制、合规要求、成本优化

参考资源