Supabase Database:专属 Postgres 实例、自动生成的 API 与 RLS 访问控制解析
Supabase 的核心理念是"每个项目就是一套专属的 Postgres 数据库"。本篇基于仓库内的产品文档(apps/www/content/md/database.md)并结合 docker/ 自托管栈的真实配置与初始化脚本,系统讲解 Supabase Database 的架构定位、自动生成的 REST/GraphQL API、连接池方案、Row Level Security 与 JWT 的集成机制、Postgres 扩展体系,以及备份、计算规格等技术细节,帮助读者理解 Supabase 数据库能力在自托管环境下的实际落地方式。
专属 Postgres,而非共享集群
官方文档对 Supabase Database 的定义非常明确:它不是"Postgres 兼容"的替代品,也不是多租户共享集群,而是每个项目独占一个可直接用任意 Postgres 客户端连接的 Postgres 实例。文档给出的定位可以概括为两点:
- Dedicated Postgres:每个项目是隔离的 Postgres 实例,而不是共享租户;
- 100% portable:可以自带已有的 Postgres 数据库,也可以随时导出迁移走。
从自托管栈的源码结构看,这一"专属实例"的定位体现在 docker-compose.yml 中:db 服务使用 supabase/postgres:17.6.1.136 镜像,数据目录通过命名卷 ./volumes/db/data:/var/lib/postgresql/data 持久化,其他服务(Studio、Auth、Storage、Realtime、meta)全部通过连接串指向同一个 POSTGRES_HOST/POSTGRES_DB。文档中提到的"连接任意 Postgres 客户端"在自托管场景下即为标准的 postgres:// 连接串;supavisor(连接池)容器将 POSTGRES_PORT(默认 5432)暴露到宿主机,客户端可像连接普通 Postgres 一样连接。
db 服务还通过挂载初始化脚本完成 Supabase 特有的数据库改造,包括 realtime.sql(创建 _realtime schema)、webhooks.sql、roles.sql、jwt.sql、_supabase.sql(内部数据分析)以及 logs.sql、pooler.sql(连接池支持)。这套初始化脚本说明:Supabase 的数据库能力全部构建在原生 Postgres 之上,扩展以 schema、角色和数据库级参数(GUC)的形式注入,不改动 Postgres 核心行为,这正是"可迁移性"承诺的底层保障。
自动生成 API:PostgREST 与 GraphQL 端点
文档将"Auto-generated APIs"列为核心特性:从数据库 schema 自动生成 REST(PostgREST)和 GraphQL(pg_graphql)API,无需编写后端代码。
在自托管栈中,这一特性由 rest 服务实现,镜像为 postgrest/postgrest:v14.12(见 docker-compose.yml)。其关键配置项均可作为实操参考:
environment:
PGRST_DB_URI: postgres://authenticator:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
PGRST_DB_SCHEMAS: ${PGRST_DB_SCHEMAS} # 默认 public,graphql_public
PGRST_DB_MAX_ROWS: ${PGRST_DB_MAX_ROWS:-1000} # 单请求最大返回行数,默认 1000
PGRST_DB_EXTRA_SEARCH_PATH: ${PGRST_DB_EXTRA_SEARCH_PATH:-public}
PGRST_DB_ANON_ROLE: anon
PGRST_JWT_SECRET: ${JWT_JWKS:-${JWT_SECRET}} # 支持明文 secret / JWK / JWKS
几个值得注意的实现细节(依据 CONFIG.md 的配置参考):
PGRST_DB_SCHEMAS决定哪些 schema 被暴露为 REST API,默认值为public,graphql_public——graphql_public的存在直接印证了文档中"GraphQL API 由 pg_graphql 生成"的说法:GraphQL 端点复用 PostgREST 的/graphql路由;PGRST_DB_MAX_ROWS默认 1000,即任何单个 REST 请求最多返回 1000 行,超出必须用分页(Range头或limit/offset);PGRST_DB_URI使用的是authenticator角色而非超级用户——这是 PostgREST 的标准安全模式,由它接管请求级角色切换(anon/authenticated/service_role);PGRST_JWT_SECRET优先读取 JWKS,回落到对称JWT_SECRET,与 Auth 服务签发 JWT 的密钥体系保持一致,从而让 REST API 的请求级鉴权与 RLS 策略联动。
rest 服务还启用了管理端口(PGRST_ADMIN_SERVER_PORT: 3001,仅限 localhost),供 Studio 等内部组件做健康检查与配置变更。
连接与连接池:Supavisor 与 PgBouncer
文档"Technical Details"部分指出:连接方式包括直连 Postgres(connection string)与通过 PgBouncer 风格的连接池(Supavisor)。仓库中两种方案都有完整实现,可对照选择。
默认方案:Supavisor
docker-compose.yml 中的 supavisor 服务(镜像 supabase/supavisor:2.9.5)是默认连接池:
ports:
- ${POSTGRES_PORT}:5432 # session/直连模式端口
- ${POOLER_PROXY_PORT_TRANSACTION}:6543 # 事务模式端口
environment:
POOLER_POOL_MODE: transaction
POOLER_DEFAULT_POOL_SIZE: ${POOLER_DEFAULT_POOL_SIZE}
POOLER_MAX_CLIENT_CONN: ${POOLER_MAX_CLIENT_CONN}
DB_POOL_SIZE: ${POOLER_DB_POOL_SIZE}
Supavisor 的入口命令会先执行迁移(/app/bin/migrate),再加载 pooler.exs 中的租户配置。POOLER_TENANT_ID 决定了客户端连接串中的"租户"参数——这也是官方客户端 session 与 transaction 两种连接模式(对应连接串中 mode=transaction 参数或不同端口)背后的机制。
可替换方案:PgBouncer
docker-compose.pgbouncer.yml 提供了一个标准 PgBouncer 覆盖文件,使用时通过 !reset null 移除 Supavisor 服务,并加入 edoburu/pgbouncer:v1.25.2-p0 容器:
environment:
DB_USER: pgbouncer # 以专用角色连接 Postgres
AUTH_TYPE: scram-sha-256
AUTH_QUERY: SELECT * FROM pgbouncer.get_auth($$1) # 按需查询其他角色口令
POOL_MODE: transaction # 可选 session/transaction/statement
DEFAULT_POOL_SIZE: ${POOLER_DEFAULT_POOL_SIZE}
MAX_CLIENT_CONN: ${POOLER_MAX_CLIENT_CONN}
其用法在文件头部注释中写明:docker compose -f docker-compose.yml -f docker-compose.pgbouncer.yml up -d,或 sh run.sh config add pgbouncer。值得注意的是 AUTH_QUERY 机制:PgBouncer 自身只持有 pgbouncer 角色的口令,转发连接时通过 get_auth() 函数按需换取目标角色的口令,避免了在配置中存放所有角色密码。该文件同时提醒:若直接暴露 Postgres 端口到外部流量,必须在网络层限制访问。
Row Level Security 与 JWT 的深度集成
文档将 RLS 描述为"使用 Postgres RLS 策略实现细粒度访问控制,并与 Supabase Auth 的 JWT 集成"。仓库中的初始化脚本展示了这套集成的具体落地方式。
JWT 设置注入数据库。jwt.sql 在数据库初始化阶段执行:
ALTER DATABASE postgres SET "app.settings.jwt_secret" TO :'jwt_secret';
ALTER DATABASE postgres SET "app.settings.jwt_exp" TO :'jwt_exp';
这把 JWT_SECRET 与 token 有效期(JWT_EXPIRY)作为数据库级 GUC 写入。RLS 策略中常用的 auth.jwt() 辅助函数(读取 request.jwt.claims 头)以及 PostgREST 的 PGRST_APP_SETTINGS_JWT_SECRET 都依赖这套密钥配置,使得策略里能直接比较 JWT 中的 raw_user_id、role 等声明:
-- 典型 RLS 策略模式:仅允许访问属于当前登录用户的行
alter table profiles enable row level security;
create policy "Users can view their own profile"
on profiles for select
using (auth.uid() = (select id from auth.users where id = auth.uid()));
角色体系由初始化脚本统一改造。roles.sql 在首次初始化时为 authenticator、pgbouncer、supabase_auth_admin、supabase_functions_admin、supabase_storage_admin 五个关键角色统一设置密码(取自 POSTGRES_PASSWORD 环境变量)。从源码结构看,anon/authenticated/service_role 三个 API 角色由 supabase/postgres 镜像本身预置,webhooks.sql 等脚本再为它们授予 supabase_functions schema 的使用权限——这意味着 RLS 策略的"三档权限模型"(匿名、登录用户、服务角色)是整个栈共享的一致概念。
Auth 服务侧的对应配置。docker-compose.yml 中 auth(GoTrue)服务的关键环境变量与 RLS 集成直接相关:GOTRUE_JWT_AUD: authenticated、GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated 决定签发的 JWT 中 aud 与 role 声明的默认值,GOTRUE_JWT_EXP 控制 token 生命周期,GOTRUE_JWT_ADMIN_ROLES: service_role 声明管理员角色。PostgREST 读到这些声明后切换到对应 Postgres 角色,RLS 策略即按角色执行,从而实现了"JWT → 角色 → 行级权限"的完整链路。
扩展体系:从 40+ 一键启用到扩展清单源码
文档声称 Supabase 支持 40 余种 Postgres 扩展(pgvector、PostGIS、pg_cron、pg_stat_statements 等)且可一键启用。仓库中 packages/shared-data/extensions.json 是扩展目录的机器可读清单,Studio 的扩展管理页面即基于此渲染:每项包含 name、comment、tags(Utility / Index / Data Type / Search / Geo 等分类)、官方文档 link 以及可选的 github_url/product 信息。示例条目:
{
"name": "pg_net",
"comment": "HTTP requests from SQL",
"tags": ["Utility"],
"link": "https://github.com/prisma/pg_net"
}
清单中涵盖了 autoinc、bloom、btree_gin、citext、cube、dblink、dict_int、fuzzystrmatch、hstore、address_standardizer(PostGIS 系)等条目,与文档"一键启用 Postgres 扩展"的描述一一对应——Studio 的启用操作最终就是执行 CREATE EXTENSION。
另一个扩展在 Webhooks 机制中直接可见:webhooks.sql 首行即 CREATE EXTENSION IF NOT EXISTS pg_net SCHEMA extensions;,说明"Database Webhooks"功能依赖 pg_net 在数据库内发起 HTTP 请求(见下一节)。
数据库 Webhooks:表事件触发 HTTP 回调
文档将"Database Webhooks"定义为:在表的增删改事件上触发 Edge Functions 或外部 HTTP 端点。其实现完整保留在 webhooks.sql 中,核心是一个 PL/pgSQL 触发器函数:
CREATE FUNCTION supabase_functions.http_request()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
DECLARE
request_id bigint;
payload jsonb;
url text := TG_ARGV[0]::text; -- 触发时传入的目标 URL
method text := TG_ARGV[1]::text; -- HTTP 方法
headers jsonb DEFAULT '{}'::jsonb; -- 自定义请求头
params jsonb DEFAULT '{}'::jsonb; -- 查询参数
timeout_ms integer DEFAULT 1000; -- 请求超时,默认 1000ms
...
该函数从 TG_ARGV 数组读取 URL、方法与可选的 headers/params/timeout,并通过 supabase_functions.hooks 表记录每次触发的审计日志(含 request_id 关联 pg_net 的请求状态)。脚本同时为 postgres、anon、authenticated、service_role 四个角色授予 supabase_functions schema 的默认权限。使用方式是在目标表上创建触发器并传入目标端点参数,例如:
create trigger notify_on_insert
after insert on orders
for each row execute function
supabase_functions.http_request('https://api.example.com/hook', 'POST');
Realtime:通过 WebSocket 订阅数据变更
文档列出 Realtime 能力为"通过 WebSockets 订阅 INSERT、UPDATE、DELETE 等变更"。自托管栈中对应 realtime 服务(镜像 supabase/realtime:v2.102.3,Elixir 实现),其 docker-compose.yml 配置展示了它如何挂在数据库上:
environment:
DB_USER: supabase_admin
DB_AFTER_CONNECT_QUERY: 'SET search_path TO _realtime' # 初始化脚本创建的 schema
DB_ENC_KEY: ${REALTIME_DB_ENC_KEY:-supabaserealtime}
SEED_SELF_HOST: "true"
DB_AFTER_CONNECT_QUERY 与 realtime.sql 创建的 _realtime schema 相呼应:Realtime 服务在该 schema 中维护复制槽(replication slot)与订阅关系,将 Postgres 的 WAL 变更翻译成 WebSocket 事件广播。RLIMIT_NOFILE: "10000" 则表明其为高并发连接场景调高文件描述符上限。需要指出:_realtime schema 只是"挂载点",实际的复制逻辑由 Realtime 服务在连接后创建,属于从源码结构可以推断的实现方式。
编辑器能力:Table Editor 与 SQL Editor
文档将 Table Editor(表格化的数据查看/编辑界面,支持关系、JSON 列与外键查找)和 SQL Editor(带自动补全与语法高亮、可保存查询)列为产品级特性。从仓库源码结构看,这两个编辑器均实现于 apps/studio 中,并依赖两个后端组件协同:
- postgres-meta(
meta服务,镜像supabase/postgres-meta:v0.96.6):提供 schema 自省、表结构、角色管理、查询执行等 REST API。CONFIG.md 明确STUDIO_PG_META_URL(如http://meta:8080)为必填项;Studio 通过它执行 SQL 编辑器的读写查询,且POSTGRES_USER_READ_WRITE(默认postgres)决定 SQL 编辑器使用的角色; - Snippets 持久化:
SNIPPETS_MANAGEMENT_FOLDER: /app/snippets挂载自./volumes/snippets,即 SQL Editor 中"保存查询"(Snippets)的落盘位置;Edge Functions 的在线编辑源文件则挂载自./volumes/functions(只读)。
技术细节总览:引擎、客户端、备份与计算规格
文档"Technical Details"一节给出了平台侧的硬性指标,汇总如下(注意:备份与计算规格描述的是 Supabase 托管平台能力,自托管部署的备份需自行规划,docker/README.md 的"Important Notes"也明确提醒默认配置不面向生产、需要自行设置备份流程):
| 维度 | 说明 |
|---|---|
| 引擎 | PostgreSQL 最新稳定版;自托管栈当前镜像为 supabase/postgres:17.6.1.136,并提供 docker-compose.pg17.yml 覆盖文件与 upgrade-pg17.sh 就地升级脚本(另有 docker-compose.pg15.yml 兼容 PG 15 部署) |
| 连接 | 直连连接串 + 连接池(默认 Supavisor,可切换 PgBouncer 事务模式) |
| 客户端库 | JavaScript、Python、Dart (Flutter)、Swift、Kotlin、C# |
| 备份 | 托管平台提供每日自动备份,Point-in-Time Recovery 为可选附加项 |
| 计算 | 从 Micro(2 核 ARM)到 16XL+(64 核 ARM 及以上)可配置,支持自动扩缩 |
其中引擎版本从源码得到双重印证:默认 compose 文件的 db 服务注释"To upgrade an existing Postgres 15 database in place, see utils/upgrade-pg17.sh"与镜像标签一致,test-pg17-upgrade.sh 则提供了升级流程的自动化测试。
周边能力:Branching、只读副本与 Pipelines
文档还列出三项面向规模化场景的能力,它们属于托管平台特性,自托管栈中无对应服务,此处按文档原样说明并标注适用范围:
- Database Branching:创建与 git 分支同步的隔离数据库分支,支持 Vercel Preview 工作流——适用于按 PR 隔离数据环境的团队协作;
- Read Replicas:跨多地域分布读流量以降低延迟、提升吞吐;
- Supabase Pipelines:将已发布的 Postgres 数据以近实时方式同步到分析型目的地。
小结与可继续深入的路径
Supabase Database 的架构主张可以浓缩为一句话:原生专属 Postgres + 数据库内注入的元能力(角色、GUC、扩展 schema)+ 围绕数据库的一组网关服务。仓库中的自托管栈完整呈现了这一架构:PostgREST 从 schema 生成 REST/GraphQL API,Supavisor/PgBouncer 承担连接池,jwt.sql/roles.sql 初始化 RLS 与 JWT 集成的基础,webhooks.sql 用 pg_net 实现表事件触发,realtime.sql 为变更广播预留 _realtime schema,extensions.json 则驱动 Studio 中的一键扩展管理。
若需继续深入,建议从以下仓库路径入手:
- docker/docker-compose.yml 与 docker/CONFIG.md:全部服务与环境变量参考;
- docker/volumes/db/:数据库初始化脚本全集(realtime、webhooks、roles、jwt、pooler、logs);
- docker/docker-compose.pgbouncer.yml:PgBouncer 替换方案;
- packages/shared-data/extensions.json:扩展目录数据源;
- apps/studio:Table Editor / SQL Editor 前端实现;
- packages/pg-meta:postgres-meta 服务(Studio 的 schema 自省与查询执行后端)源码。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00