Supabase 是一个开源的 Firebase 替代品,它提供了一整套后端即服务 (BaaS) 工具,旨在帮助开发者更快地构建应用。Its core philosophical difference from Firebase lies in its foundation: Supabase is built entirely around PostgreSQL作为其核心数据存储,并围绕 PostgreSQL 提供了认证、实时订阅、存储和边缘函数等一系列服务。这使得开发者可以使用熟悉的 SQL 语言来管理数据和定义业务逻辑,同时享受现代化 BaaS 服务的便利。

核心思想:
以强大的开源关系型数据库 PostgreSQL 为中心,提供一套集成且可扩展的 BaaS 服务,实现从数据库到 API、认证、实时功能的无缝连接。


一、为什么需要 Supabase?

传统上,构建一个功能完善的应用程序需要开发者处理大量的后端基础设施工作,包括:

  1. 数据库管理:选择、设置、维护数据库,编写 CRUD API。
  2. 用户认证和授权:实现用户注册、登录、密码重置、JWT 管理、权限控制。
  3. 实时功能:处理 WebSocket 连接,响应数据库变更。
  4. 文件存储:设置对象存储服务,管理文件上传下载。
  5. 业务逻辑:编写和部署后端服务器代码。

这些任务通常耗时耗力,且需要专业的运维知识。Supabase 的目标是通过将这些常用后端服务预先集成并提供开箱即用的解决方案,让开发者能够:

  • 加速开发:快速启动项目,专注于前端和核心业务逻辑。
  • 降低复杂性:无需管理复杂的服务器和数据库基础设施。
  • 利用现有知识:通过 PostgreSQL 和 SQL,可以有效利用已有的数据库技能。
  • 保持灵活性:作为开源项目,支持自托管,避免供应商锁定。

二、Supabase 核心组件

Supabase 生态系统由一系列精心选择和集成的开源工具组成,它们协同工作,共同提供全面的 BaaS 功能。

2.1 PostgreSQL 数据库

  • 定义:作为 Supabase 的核心,它是一个功能强大、高度可扩展且世界领先的开源关系型数据库。所有数据都存储在 PostgreSQL 中。
  • 特性:支持复杂查询、事务、视图、存储过程、触发器、JSONB 类型等。通过 PostgreSQL 的扩展能力,Supabase 还能集成额外功能。
  • 优势:开发者可以利用熟悉的 SQL 知识,并通过 SQL 直接管理数据、定义表结构、实现业务逻辑。

2.2 GoTrue (Authentication)

  • 定义:一个基于 JWT (JSON Web Tokens) 的用户认证服务。它管理用户注册、登录、会话管理、密码重置等功能。
  • 特性
    • 支持电子邮件/密码认证。
    • 支持数十种第三方 OAuth 提供商(如 Google, GitHub, Apple 等)。
    • 自动生成 JWT 令牌,并与 PostgreSQL 的行级安全性 (Row Level Security, RLS) 无缝集成。
    • 提供了易于使用的 API 和 SDK。

2.3 PostgREST (RESTful API)

  • 定义:一个独立的 Web 服务器,可以将 PostgreSQL 数据库直接转换为高性能的 RESTful API。
  • 特性
    • 即时 API:无需编写任何后端代码,只需定义好数据库表和视图,PostgREST 就会自动生成对应的 RESTful API。
    • 单点真实数据源:API 直接与数据库交互,确保数据一致性。
    • 权限集成:与 PostgreSQL 的 RLS 机制和 GoTrue 生成的 JWT 令牌完美结合,实现细粒度的授权控制。

2.4 Realtime (实时服务)

  • 定义:一个 WebSocket 服务器,用于监听 PostgreSQL 数据库的变更事件(插入、更新、删除)。
  • 特性
    • 数据库变更订阅:客户端可以通过 WebSocket 连接订阅特定表或视图的变更,实时获取数据更新通知。
    • 基于 PostgreSQL 的逻辑复制:底层利用 PostgreSQL 的逻辑复制 (Logical Replication) 功能捕获数据库变更。
    • 轻松实现实时功能:如聊天应用、实时仪表盘、协作工具等。

2.5 Storage (对象存储)

  • 定义:一个 S3 兼容的对象存储服务,用于存储非结构化数据,如图片、视频、文档等。
  • 特性
    • 文件上传下载:通过 API 轻松管理文件的上传和下载。
    • 访问控制:与 GoTrue 和 RLS 集成,实现细粒度的文件访问权限控制。
    • 私有/公共存储桶:支持创建不同访问权限的存储桶。

2.6 Edge Functions (边缘函数)

  • 定义:基于 Deno Runtime 构建的 Serverless 无服务器函数服务,可以在全球边缘网络部署,提供低延迟的计算能力。
  • 特性
    • 自定义后端逻辑:用于实现复杂的业务逻辑、数据转换、与第三方服务集成等。
    • TypeScript/JavaScript 支持:利用 Deno 的原生 TypeScript 支持。
    • 快速部署和执行:在靠近用户的地方执行代码,减少延迟。

2.7 Dashboard (控制面板)

  • 定义:Supabase 提供的一个直观的 Web UI,用于管理项目、数据库、认证用户、存储文件、函数等所有服务。
  • 特性
    • 数据库浏览器:直观地查看和编辑表数据。
    • SQL 编辑器:直接在浏览器中执行 SQL 查询。
    • 认证用户管理:管理用户、会话、OAuth 配置。
    • 存储文件浏览器:管理上传的文件。
    • API 文档:自动生成 API 文档。

三、Supabase 主要特性

  1. 开源与自托管选项:所有核心组件都是开源的,允许开发者完全控制和自托管,避免供应商绑定。
  2. PostgreSQL-Centric:充分利用 PostgreSQL 的强大功能和生态,包括其丰富的扩展(如 PostGIS 用于地理空间数据,pg_cron 用于调度任务)。
  3. 即时 RESTful API:通过 PostgREST 自动从数据库模式生成 API,极大地简化了后端开发。
  4. 实时订阅 (Realtime Subscriptions):通过 WebSocket 监听数据库的实时变更,轻松构建动态应用。
  5. 强大且细粒度的安全控制
    • 行级安全性 (Row Level Security, RLS):PostgreSQL 的内置功能,允许在数据库层面定义哪些用户可以访问或修改哪些数据行,与 JWT 无缝集成。
    • GoTrue (Auth):提供全面的用户认证解决方案。
  6. 文件存储能力:S3 兼容的对象存储,结合认证和 RLS 实现安全的文件管理。
  7. 边缘函数 (Edge Functions):提供高性能的无服务器函数,用于自定义后端逻辑。
  8. 数据库迁移 (Database Migrations):SQL-优先的迁移管理方案,方便版本控制和团队协作。

四、Supabase 的优缺点

4.1 优点

  1. 开发效率高:通过即时 API 和预置服务,大大缩短了开发周期,特别适合快速原型开发和 MVP。
  2. 降低运维成本:托管服务免去了数据库、服务器、认证服务等基础设施的部署和维护工作。
  3. 强大的 PostgreSQL 后盾:受益于 PostgreSQL 的数据完整性、事务支持和丰富的扩展,提供了坚实的数据基础。
  4. 高度可定制和可扩展:由于是开源项目,且基于 PostgreSQL,开发者可以通过 SQL 或自定义函数进行深度定制。
  5. 良好的安全性:RLS 结合 JWT 提供了企业级的细粒度授权控制。
  6. 活跃的社区与文档:拥有庞大的开发者社区和详尽的官方文档。

4.2 缺点

  1. 潜在的供应商锁定:虽然是开源,但深度依赖其托管服务可能会导致迁移成本。
  2. 性能考量:对于极高并发或特定优化的场景,可能不如完全自定义的后端服务灵活。PostgREST 的通用性在某些复杂查询上可能没有手写 API 效率高。
  3. 学习曲线:对于不熟悉 PostgreSQL 或 RLS 的开发者,需要一定的学习成本。
  4. 功能限制:与 Firebase 相比,某些特定功能(如 Firestore 或 Cloud Functions 的某些高级特性)可能需要通过 Edge Functions 自行实现或寻找替代方案。
  5. 数据中心地域选择:托管服务的数据中心选择可能受限,影响延迟和合规性。

五、适用场景

Supabase 适用于各种需要快速开发和部署后端服务的场景:

  • 全栈 Web/移动应用:作为任何使用前端框架 (React, Vue, Angular, Svelte) 或移动应用 (React Native, Flutter, Swift, Kotlin) 的后端。
  • 快速原型 (Rapid Prototyping):用于验证产品想法或进行概念验证 (PoC)。
  • 最小可行产品 (MVP) 开发:在短时间内构建和发布核心功能。
  • 内部工具和仪表盘:快速搭建管理界面或数据可视化应用。
  • 数据密集型应用:受益于 PostgreSQL 的强大查询能力和数据完整性。
  • 支持实时协作的应用:如聊天应用、在线文档编辑器等。
  • 需要细粒度权限控制的应用:通过 RLS 轻松实现复杂权限模型。

六、Go 语言客户端交互示例 (概念性)

虽然 Supabase 提供了 Go 语言的 SDK,但这里的示例将侧重于使用 Go 语言通过 HTTP (模拟客户端 SDK) 与 Supabase 的 RESTful API 进行交互,展示其API的核心概念。

假设我们有一个名为 users 的表,包含 id, name, email 字段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package main

import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
)

// User represents a user in the database
type User struct {
ID int `json:"id,omitempty"`
Name string `json:"name"`
Email string `json:"email"`
}

func main() {
// 从环境变量获取 Supabase 配置
supabaseURL := os.Getenv("SUPABASE_URL")
supabaseKey := os.Getenv("SUPABASE_ANON_KEY") // 通常用于公共读写或通过RLS控制

if supabaseURL == "" || supabaseKey == "" {
fmt.Println("Error: SUPABASE_URL and SUPABASE_ANON_KEY environment variables must be set.")
os.Exit(1)
}

// 1. 获取所有用户 (假设RLS允许匿名读取)
fmt.Println("--- Fetching all users ---")
getUsersURL := fmt.Sprintf("%s/rest/v1/users", supabaseURL)
req, err := http.NewRequest("GET", getUsersURL, nil)
if err != nil {
fmt.Printf("Error creating request: %v\n", err)
return
}
req.Header.Add("apikey", supabaseKey)
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", supabaseKey)) // 匿名密钥也可以作为Bearer Token

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Printf("Error sending request: %v\n", err)
return
}
defer resp.Body.Close()

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("Error reading response body: %v\n", err)
return
}

if resp.StatusCode != http.StatusOK {
fmt.Printf("Failed to fetch users: %s, Status: %d\n", string(body), resp.StatusCode)
return
}

var users []User
if err := json.Unmarshal(body, &users); err != nil {
fmt.Printf("Error unmarshaling users: %v\n", err)
return
}
fmt.Printf("Fetched users: %+v\n", users)


// 2. 插入一个新用户 (假设RLS允许匿名插入)
fmt.Println("\n--- Inserting a new user ---")
newUser := User{Name: "Alice", Email: "alice@example.com"}
jsonUser, _ := json.Marshal(newUser)

postUserURL := fmt.Sprintf("%s/rest/v1/users", supabaseURL)
req, err = http.NewRequest("POST", postUserURL, bytes.NewBuffer(jsonUser))
if err != nil {
fmt.Printf("Error creating POST request: %v\n", err)
return
}
req.Header.Add("apikey", supabaseKey)
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", supabaseKey))
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Prefer", "return=representation") // 请求返回新创建的记录

resp, err = client.Do(req)
if err != nil {
fmt.Printf("Error sending POST request: %v\n", err)
return
}
defer resp.Body.Close()

body, err = ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("Error reading POST response body: %v\n", err)
return
}

if resp.StatusCode != http.StatusCreated {
fmt.Printf("Failed to insert user: %s, Status: %d\n", string(body), resp.StatusCode)
return
}

var createdUsers []User // 插入成功会返回一个数组
if err := json.Unmarshal(body, &createdUsers); err != nil {
fmt.Printf("Error unmarshaling created users: %v\n", err)
return
}
fmt.Printf("Inserted user: %+v\n", createdUsers[0])


// 3. 使用过滤器查询用户 (例如按 email)
fmt.Println("\n--- Querying user by email ---")
queryUserURL := fmt.Sprintf("%s/rest/v1/users?email=eq.alice@example.com", supabaseURL) // eq.表示等于
req, err = http.NewRequest("GET", queryUserURL, nil)
if err != nil {
fmt.Printf("Error creating query request: %v\n", err)
return
}
req.Header.Add("apikey", supabaseKey)
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", supabaseKey))

resp, err = client.Do(req)
if err != nil {
fmt.Printf("Error sending query request: %v\n", err)
return
}
defer resp.Body.Close()

body, err = ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("Error reading query response body: %v\n", err)
return
}

if resp.StatusCode != http.StatusOK {
fmt.Printf("Failed to query user: %s, Status: %d\n", string(body), resp.StatusCode)
return
}

var queriedUsers []User
if err := json.Unmarshal(body, &queriedUsers); err != nil {
fmt.Printf("Error unmarshaling queried users: %v\n", err)
return
}
fmt.Printf("Queried user: %+v\n", queriedUsers)

}

运行此示例前,请确保:

  1. 你有一个正在运行的 Supabase 项目。
  2. 在你的 Supabase 控制台的 “Project Settings -> API” 中找到 Project URLanon public Key。
  3. 在你的环境中设置 SUPABASE_URLSUPABASE_ANON_KEY 环境变量。
  4. 在你的 public schema 中有一个名为 users 的表,结构类似 id INT PRIMARY KEY, name TEXT, email TEXT UNIQUE
  5. 确保 public.users 表的 RLS 策略允许匿名用户进行 SELECT 和 INSERT 操作,例如:
    1
    2
    3
    4
    5
    6
    -- 启用RLS
    ALTER TABLE public.users ENABLE ROW LEVEL SECURITY;
    -- 允许匿名用户选择
    CREATE POLICY "Allow anon select" ON public.users FOR SELECT TO anon USING (TRUE);
    -- 允许匿名用户插入
    CREATE POLICY "Allow anon insert" ON public.users FOR INSERT TO anon WITH CHECK (TRUE);

七、总结

Supabase 通过将强大的 PostgreSQL 数据库与一系列集成且开箱即用的后端服务相结合,为开发者提供了一个极具吸引力的 BaaS 平台。它不仅显著提高了开发效率,降低了运维门槛,还通过其开源特性和 PostgreSQL 的灵活性,提供了超越传统 BaaS 产品的可定制性和控制能力。无论是快速启动新项目、构建复杂的数据驱动应用,还是寻求 Firebase 的开源替代品,Supabase 都是一个值得深入探索和考虑的优秀选择。