Supabase Studio Firebase Wrapper 指南:用 Postgres FDW 直接读取 Firebase Auth 用户与 Firestore 数据
在 Supabase 生态中,Firebase 是一个常见的"外部数据源"——很多团队既有存量的 Firebase 项目(Auth 用户、Firestore 文档),又希望在新应用中使用 Postgres。本篇围绕 Studio 集成页中的 Firebase Wrapper 概览文档(overview.md)展开,讲清它的定位:一个基于 Postgres 外部数据包装器(Foreign Data Wrapper,FDW)的集成,让你在 Postgres 里直接查询 Firebase 数据。读完本文,你将掌握:Firebase Wrapper 支持的连接对象及其完整参数(默认值、加密策略)、对应的 SQL 创建方式、FDW 的安全边界,以及它与 Firebase 第三方登录、Firestore 数据迁移这两个相邻功能的区别。
Firebase Wrapper 是什么
原始概览文档只有三句话,但信息密度很高,它给出了两个关键定义:
Firebase is an app development platform built around non-relational technologies. The Firebase Wrapper supports connecting to below objects. The Firebase Wrapper is a foreign data wrapper which allows you to read data from Firebase within your Postgres database.
翻译过来即:Firebase 是围绕非关系型技术构建的应用开发平台,而 Firebase Wrapper 是一个外部数据包装器(FDW),让你可以在 Postgres 数据库内部读取 Firebase 的数据。
FDW 是 Postgres 的核心能力:把外部系统里的数据映射成本地"外表"(foreign table),用标准 SQL 查询,但数据实际仍存放在远端。Supabase 将这一能力扩展成开放的 Wrappers 框架,Firebase Wrapper 就是其中一员。按该框架的概念体系:
- Remote Server:你要访问的外部系统,这里是某个 Firebase 项目。同一个数据库里可以创建多个 Remote Server 连接不同的 Firebase 项目;
- Foreign table:数据库中的一张表,映射到 Remote Server 里的某份数据(对应本文下面介绍的 Users 与 Firestore Collection 两类对象)。外表查询时数据不落库,始终从 Firebase 实时拉取;
- ETL / QETL:你可以用
insert into ... select from <外表>把 Firebase 数据导入本地表(批量 ETL,可配合pg_cron定时),也可以直接把外表当普通表join你的业务表做实时查询(QETL,即"按需查询")。
从源码结构看,概览文档本身只是 Studio 的一个静态展示条目,它由一个注册表按需加载:overviews.ts 中 firebase_wrapper 键对应一条动态 import,loadIntegrationOverview(integrationId) 在用户打开 Integrations 页面的 Firebase 条目时才返回这段 Markdown 原文。该文件顶部的注释还解释了为何必须写成字符串字面量导入——webpack/turbopack 与 Vite/Rolldown 对模板字符串动态导入的处理不一致,动态写法会在 TanStack 构建中抛 Failed to resolve module specifier。
Studio 中 Firebase Wrapper 的参数定义
概览文档说 "supports connecting to below objects"(支持连接以下对象),具体对象、参数、默认值并不在 Markdown 里,而是定义在 Studio 的常量 Wrappers.constants.ts 中。该条目声明了完整的技术指纹:
| 字段 | 值 | 含义 |
|---|---|---|
name |
firebase_wrapper |
集成 id(即 static-data 目录名) |
handlerName |
firebase_fdw_handler |
FDW 的 handler 函数名 |
validatorName |
firebase_fdw_validator |
FDW 的 validator 函数名 |
extensionName |
FirebaseFdw |
对应 Wrappers 框架的扩展名 |
label |
Firebase |
界面显示名 |
description |
Backend-as-a-Service with real-time database |
界面描述 |
categories |
['devtools', 'auth'] |
集成分类(开发工具 / 认证) |
Remote Server 的两个必填选项
在 SQL 层面,Remote Server 对应一个 CREATE SERVER。Firebase Wrapper 的 server 级选项只有两个,且都必填:
| 选项名 | 标签 | 是否加密存储 | 说明 |
|---|---|---|---|
project_id |
Project ID | 否 | Firebase 项目的 ID,只允许字母、数字与连字符 |
sa_key_id |
Service Account Key | 是(encrypted: true,多行文本域) |
服务账号密钥的完整 JSON,用于以 Admin 权限访问该 Firebase 项目 |
sa_key_id 标记为加密存储,意味着 Studio 只会在服务端加密保存该密钥,不会明文回显。服务账号 JSON 需要在 Firebase 控制台的 Project Settings → Service Accounts 中生成(Studio 的常量里把该入口标注为 urlHelper 指向 Firebase Admin SDK 的初始化文档)。
支持的对象一:Users(Firebase Auth 用户)
常量中第一个表模板 label 为 Users,描述为 "Shows your Firebase users",即把 Firebase Auth 的用户列表映射为外表。列结构固定为四列:
| 列名 | 类型 | 说明 |
|---|---|---|
uid |
text |
Firebase 用户 ID |
email |
text |
用户邮箱 |
created_at |
timestamp |
创建时间 |
attrs |
jsonb |
其余字段(自定义声明、手机号、头像等) |
对应的表级 options 及其默认值:
| 选项 | 默认值 | 可否编辑 | 说明 |
|---|---|---|---|
object |
auth/users |
否(editable: false) |
锁定的 Auth 用户端点 |
base_url |
https://identitytoolkit.googleapis.com/v1/projects |
可编辑 | Identity Toolkit API 前缀 |
limit |
10000 |
可编辑 | 单次拉取行数上限 |
支持的对象二:Firestore Collection(任意集合)
第二个表模板 label 为 Firestore Collection,描述为 "Map to a Firestore collection",即把 Firestore 中任意集合映射为外表。列结构同样四列:
| 列名 | 类型 | 说明 |
|---|---|---|
name |
text |
文档标识 |
created_at |
timestamp |
创建时间 |
updated_at |
timestamp |
更新时间 |
attrs |
jsonb |
文档正文(嵌套字段拍平到 jsonb) |
表级 options:
| 选项 | 默认值 / 占位符 | 可否编辑 | 说明 |
|---|---|---|---|
object |
占位符 firestore/[collection_id] |
是(必填) | firestore/ 前缀 + 集合 ID,决定映射哪个集合 |
base_url |
https://firestore.googleapis.com/v1beta1/projects |
是(必填) | Firestore Admin API 前缀 |
limit |
10000 |
是(必填) | 单次拉取行数上限 |
两类对象的共同点值得注意:文档正文都是 jsonb 兜底(attrs 列)。这呼应了概览文档开头"Firebase 是非关系型平台"的判断——非结构化的文档字段被序列化进 attrs,需要时用 Postgres 的 JSON 操作符(->、->>、jsonb_path_exists 等)再行拆解。
SQL 实战:创建 server、外表并查询
Wrappers 框架在平台侧预装了扩展,实际使用时按标准 FDW 语法创建 server 与外表即可。以下 SQL 与上面常量中的字段名、默认值一一对应(扩展名取自常量中的 extensionName: 'FirebaseFdw',SQL 中按 Postgres 扩展命名惯例写作 firebase_fdw):
-- 1. 扩展(Studio 常量中 extensionName 为 FirebaseFdw)
create extension if not exists firebase_fdw;
-- 2. Remote Server:project_id + sa_key_id(加密存储的服务账号 JSON)
create server if not exists firebase_server
foreign data wrapper firebase_fdw
options (
project_id 'my-firebase-project',
sa_key_id '<service-account-key-json>'
);
-- 3a. 外表:Firebase Auth 用户(object 固定为 auth/users)
create foreign table firebase.firebase_users (
uid text,
email text,
created_at timestamp,
attrs jsonb
)
server firebase_server
options (
object 'auth/users',
base_url 'https://identitytoolkit.googleapis.com/v1/projects',
limit '10000'
);
-- 3b. 外表:某个 Firestore 集合(object 为 firestore/[collection_id])
create foreign table firebase.firestore_orders (
name text,
created_at timestamp,
updated_at timestamp,
attrs jsonb
)
server firebase_server
options (
object 'firestore/orders',
base_url 'https://firestore.googleapis.com/v1beta1/projects',
limit '10000'
);
创建之后,QETL 式的实时查询就是普通 SQL,例如把 Firestore 里的订单与本地 auth.users 关联:
select
auth.users.id as user_id,
o.name as order_id,
o.attrs ->> 'total' as total
from
firebase.firestore_orders o
join auth.users
on auth.users.email = o.attrs ->> 'email'
where
o.attrs ? 'vip';
也可以走批量 ETL,把拉取结果固化成本地表(配合 Wrappers 概览文档中介绍的 pg_cron 调度方式):
insert into public.firebase_users_snapshot (uid, email, created_at, attrs)
select uid, email, created_at, attrs
from firebase.firebase_users;
安全边界:FDW 没有行级安全
FDW 外表不提供 Row Level Security,这是 Wrappers 官方文档反复强调的安全红线(见 Foreign Data Wrappers 概览的 Security 一节)。落到 Firebase Wrapper 上,应遵循三条规则:
- 私有 schema:所有 Firebase 外表放在独立 schema(如示例中的
firebase)中,且该 schema 不要加入 API 配置里的 "Additional Schemas",避免被 PostgREST 直接暴露; - 按需开窗:需要对外提供数据时,在
publicschema 建security definer函数,在函数内对attrs等列做过滤后再返回; - 收紧执行权限:
security definer函数默认anon也能调用,必须revoke后只授权给目标角色。
以 Firebase 用户为例:
-- public 侧开窗函数:只暴露邮箱前缀匹配的脱敏字段
create function public.list_firebase_emails(prefix text)
returns table (email text)
language sql
security definer set search_path = ''
as $$
select u.email
from firebase.firebase_users u
where u.email like prefix || '%'
$$;
-- 收回默认执行权,只授权给已登录用户
revoke execute on function public.list_firebase_emails(prefix text) from public, anon;
grant execute on function public.list_firebase_emails(prefix text) to authenticated;
客户端即可通过 supabase.rpc('list_firebase_emails', { prefix: 'acme.' }) 访问,而不直接接触外表。
易混淆概念辨析:数据 Wrapper ≠ 第三方登录 ≠ 数据迁移
Firebase 与 Supabase 的交叉点有三个,容易混淆,这里用仓库中的实现代码做个区分:
- Firebase Wrapper(本文主题):数据层集成,用 FDW 读取 Firebase 数据,属于 Integrations 页的 Wrappers 条目,分类为
devtools与auth; - Firebase Auth 第三方登录:认证层集成,让 Firebase 签发的 ID Token 直接当作 Supabase 的 JWT 使用。对应实现是 CreateFirebaseAuthDialog.tsx:表单校验
firebaseProjectId后,提交oidcIssuerUrl: https://securetoken.google.com/${projectId},即注册 Firebase 项目作为 OIDC issuer——它不拉取任何数据,只是声明"接受哪个项目的令牌"; - Firestore 一次性迁移:若目标是彻底搬走 Firestore 数据而非持续同步,仓库中另有专门的迁移指南 Migrate from Firebase Firestore to Supabase:使用社区的
firebase-to-supabase工具链,firestore2json.js <collectionName> [<batchSize>] [<limit>]导出集合为 JSON(batchSize 默认 1000),json2supabase.js再按none/smallserial/serial/bigserial/uuid/firestore_id主键策略导入 Postgres,并支持自定义 hook 把嵌套文档拆分成多张表。
小结
Firebase Wrapper 概览文档虽然只有三行,但它钉死了这个集成的本质:FDW。围绕这个本质,从 Wrappers.constants.ts 的常量可以还原出完整的操作面——server 级 project_id + 加密的 sa_key_id 两个必填项,两类可连接对象(auth/users 用户表与 firestore/[collection_id] 集合表),以及 base_url、limit(默认 10000)等表级选项。配合 FDW 无 RLS 的安全约束(私有 schema + security definer 开窗函数),Firebase Wrapper 就是一条"用 SQL 把 Firebase 当成 Postgres 的邻居"的完整链路:实时 join 走 QETL,周期性固化走批量 ETL,彻底搬迁则转向迁移工具链。
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 StartedRust0626
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