Supabase — 开源后端即服务¶
一句话说明: Supabase 是 Firebase 的开源替代方案,提供 PostgreSQL 数据库、身份认证、文件存储、实时订阅和 Edge Functions 的一站式后端平台。
为什么要学¶
- 面试加分 — 展示你理解 BaaS 架构、Serverless 模式和全栈快速交付能力
- 实用性 — 免费额度足够个人项目,从原型到生产无缝扩展
- 开源 — 可自托管,避免厂商锁定;基于 PostgreSQL,SQL 技能通用
- 全栈整合 — 前端直连数据库(RLS 安全),无需写传统后端 CRUD
核心概念详解¶
Supabase vs Firebase¶
白话: Firebase 是 Google 的闭源 BaaS,用 NoSQL。Supabase 是开源版本,用 PostgreSQL(关系型),对开发者更友好。
| 对比项 | Firebase | Supabase |
|---|---|---|
| 数据库 | Firestore (NoSQL) | PostgreSQL (SQL) |
| 开源 | 否 | 是 |
| 自托管 | 不支持 | 支持 |
| 实时 | 原生 | Realtime + Postgres Changes |
| Auth | Firebase Auth | GoTrue (兼容 OAuth) |
| 存储 | Cloud Storage | S3 兼容 |
| Functions | Cloud Functions | Edge 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)。适合聊天、协作编辑、实时仪表盘。
安装与配置¶
云端使用(最简单)¶
- 访问 https://supabase.com → 注册
- 创建新项目(选区域、设密码)
- 获得项目 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 安装¶
快速上手¶
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/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" } }
)
})
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;
7. 数据库迁移¶
# 创建迁移
supabase migration new add_priority_column
# 编辑迁移文件
# supabase/migrations/20240101000000_add_priority_column.sql
# 应用迁移
supabase db push
# 重置数据库
supabase db reset
常见问题与排错¶
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 查询返回空数组 | RLS 未配置/策略不匹配 | 检查 RLS 策略,临时禁用测试 |
permission denied | RLS 阻止了操作 | 添加对应操作的 policy |
| 实时不触发 | 表未开启 Replication | Dashboard → Database → Replication → 启用表 |
| Auth token 过期 | 默认 1 小时 | 客户端自动刷新,检查 session 管理 |
| Edge Function 冷启动 | 首次调用需初始化 | 使用 warm-up 请求或接受延迟 |
面试高频考点¶
- RLS 为什么重要?
前端直连数据库时,RLS 是安全屏障;无需中间后端层做权限检查
Supabase 的架构组成?
PostgreSQL + PostgREST + GoTrue(Auth) + Realtime + Storage + Edge Functions
什么时候不该用 BaaS?
复杂业务逻辑、需要事务一致性、已有成熟后端团队
实时功能的实现原理?
PostgreSQL logical replication → Realtime server → WebSocket 推送
自托管 vs 云服务的取舍?
- 云:零运维、自动备份;自托管:完全控制、合规要求、成本优化