教程 搜索 / 发布与基础设施 / 选择 BaaS:Firebase 对比 Supabase
📝 文字 ● 初级 更新于 2026-05-13

选择 BaaS:Firebase 对比 Supabase

Backend-as-a-Service 是一个捆绑包:认证、数据库、存储、函数、实时推送,全部隐藏在一个 SDK 背后。你买的不是数据库,而是永远不必操心底层如何托管的权利。Firebase 和 Supabase 是两个默认选项——数据模型不同,迁出成本不同,定价曲线相近。本文教你如何做出选择。

"BaaS"——backend-as-a-service——这个类别名称,实际上代表着对应用程序构建方式的一种押注:一个典型的应用需要认证、数据库、文件上传,以及让这些模块通过网络互相通信的机制。传统做法是自己把这些拼起来——Postgres 存数据、Passport / NextAuth 做认证、S3 做存储、一个 API 服务器做粘合,再加三四个配置文件让它们保持同步。BaaS 的押注是:接受某家厂商预先组装好的四合一方案,能让你更快地把同样的产品交付出去。你写的基础设施代码更少,认证开箱即用,SDK 就是你要 import 的一切。代价是:厂商掌控了数据模型和 API 的形态。

Firebase(Google,2011 年创建,2014 年收购)是历史更悠久的选项。它的数据库是 Firestore——一个内置实时同步的 NoSQL 文档存储,你把 JSON 格式的文档写入集合,SDK 会把更新推送到每个正在查看的客户端。Supabase(独立公司,2020 年创建)是"Firebase,但用 Postgres"——同样封装了认证、存储、实时推送和函数,但底层数据库是真正的 Postgres,支持 SQL、JOIN 和外键。这个区别看起来不大,却是在这个技术栈上你要做的最重要的决策:Firestore 优化的是"在海量客户端上实时展示符合条件的文档";Postgres 优化的是"让你在日后想到任何问题时都能查到答案"。选择与你写查询的方式相匹配的数据模型。

本教程涵盖决策过程(这是最主要的工作)、真正重要的几个维度(大多数清单都会漏掉它们),以及两者各自可运行的"读取一行数据 + 检查登录状态"hello-world 示例。读完之后,你会知道该选哪个,也会验证基本功能在你的选择下确实可以运行。从 hello-world 到真实应用,之后的工作不过是同一套 SDK 的延伸。

你将学到什么

前提条件:你熟悉某个前端框架(React、Vue、Svelte 或原生 JS 均可,两个 SDK 都支持);尝试 Firebase 需要 Google 账号;尝试 Supabase 需要 GitHub 账号或邮箱注册。hello-world 示例在本地运行,无需部署。

第 1 步:你实际上在购买什么

1

五项服务封装在一个 SDK 里

Firebase 和 Supabase 都将相同的五项能力封装在一个 SDK 和一套凭据之后:

  • 认证(Authentication)——邮箱/密码、Google / Apple / GitHub OAuth、魔法链接、手机 OTP。SDK 让你一行代码就能判断用户是否已登录。
  • 数据库(Database)——应用数据的存储位置。Firebase = Firestore(NoSQL 文档);Supabase = Postgres(关系型行)。
  • 存储(Storage)——文件上传。头像、附件、用户生成的图片。两者都提供桶状 API;Firebase 底层是 Google Cloud Storage,Supabase 底层是 S3。
  • 实时(Realtime)——数据变更时向客户端推送更新。就是那个"计数器在所有地方同步更新"的演示效果。Firestore 默认对所有读取开启;Supabase 可按表选择开启。
  • 函数(Functions)——由事件或 HTTP 触发的服务端代码。Firebase Cloud Functions(Node)和 Supabase Edge Functions(Deno)。

你在托管费之外真正为之付费的,是所有五项都绑定到同一套用户身份这件事。已登录用户的照片上传(存储)之所以被允许,是因为数据库里有一条行级别的规则,引用了他们的认证令牌(认证)。自己搭建意味着要手动编写这套连接逻辑;用 BaaS,SDK 直接把接好线的版本交给你。

第 2 步:数据模型问题(这才是关键决策)

2

Firestore 为固定形态优化;Postgres 为变化优化

Firebase 和 Supabase 之间最重要的区别就是数据库。选错了,之后每新增一个功能都是在跟 schema 较劲。

Firestore(Firebase)——NoSQL 文档。数据存储在由文档组成的集合中,文档就是 JSON。没有 JOIN,没有外键,没有迁移——schema 只存在于你的代码里。查询很简单("这个集合中字段 X 等于 Y 的文档"),在大规模场景下非常快,但任何涉及多表的操作(类似 SQL JOIN)要么需要把数据反范式化写进文档,要么在客户端拉取多个集合后自行处理。实时同步默认对所有读取开启,这是 Firebase 的招牌特性。

Postgres(Supabase)——关系型行。数据存储在带有类型列和预定义 schema 的中。JOIN 存在,外键存在,SQL 是查询语言;Supabase SDK 在上面封装了一个查询构建器,但你也可以在需要时直接写原生 SQL。schema 变更通过迁移文件管理——存放在仓库中的文本文件,负责将数据库从一个形态演变到另一个形态。实时功能需按表选择开启。

有一条经验法则出奇地管用:如果你的应用感觉像"一个有评论和点赞的帖子流",Firestore 就够了。如果感觉像"用户、项目、任务、分配,加上跨所有数据的报表",你会跟 Firestore 不断较劲,Postgres 才是赢家。大多数真实的消费者应用,即便最初看起来像前者,经过一年的迭代都会演变成后者的形态。这种倾向是 2026 年团队将 Supabase 作为默认选择的主要原因。

第 3 步:第一周之后真正重要的维度

3

两份 SDK 入门教程没有告诉你的事

维度FirebaseSupabase
查询能力 有限:只能在一个字段上过滤,一个有索引的字段上做等值或范围查询。不支持 JOIN,不支持 GROUP BY 完整 SQL。支持 JOIN、窗口函数、递归 CTE、全文搜索。
Schema 变更 无。随时添加字段;数据是否有效完全取决于应用代码。 迁移文件(存放在仓库中的文本文件,应用到数据库)。工作量更大,但更安全。
认证提供商 邮箱、手机、Google、Apple、Facebook、Twitter、GitHub、匿名、自定义 JWT。 邮箱/密码、魔法链接、Google、Apple、GitHub、GitLab、Bitbucket、Slack、Discord、Twitter、Microsoft、Notion、Spotify、Twitch、匿名、手机、SAML。
实时推送 默认对所有读取开启,是产品的核心亮点。 按表选择开启;底层使用 Postgres 逻辑复制。稳定,但不是默认行为。
行级别安全 Firestore 安全规则——一套小型 DSL。功能强大,但是专有的。 Postgres RLS——SQL。功能强大、可移植,是业界标准。
迁出成本 高。数据以 Firestore 专有格式存储;虽然有导出工具,但在其他技术栈上重建应用意味着几乎要重写所有查询。 低至中等。数据是 Postgres——pg_dump 后可以在任意地方恢复。认证和存储是厂商专有的,但通常掌握最大筹码的数据库是可移植的。
本地开发 Firebase Emulator Suite——在本地运行 Firestore、Auth、Functions。成熟、文档完善。 Supabase CLI——在 Docker 中本地运行 Postgres + Auth + Storage + Studio。成熟、文档完善。
自托管 不支持。Firebase 是仅限 Google 的服务。 支持。Supabase 是开源的;你可以在自己的 Postgres + 认证服务器上运行整套系统。大多数人不这么做,但你可以。
厂商锁定(主观) 显著。心智模型和查询方式无法迁移。 适中。Postgres 技能和 SQL 可以迁移;Supabase 专有的辅助方法则不能。

迁出成本这一行比大多数人预想的更重要。成功的应用会产生当初选择 BaaS 时没有预料到的新需求。用 Supabase,"我需要迁出"意味着"pg_dump,然后让应用连接另一个 Postgres"。用 Firebase,则是一个以月为单位计算的项目。

第 4 步:定价——关注曲线,不只是标题数字

4

两者都是免费到某个临界点;理解那个悬崖在哪里

两者都有慷慨的免费层,足以支撑个人项目和小型 SaaS。两者都会在超出配额时切换到付费计划,然后在基础计划之上按用量计费。

Firebase(Spark → Blaze)。Spark 计划免费,包含 1 GiB Firestore、1 GB 存储、10 GB 传输、每日 5 万次 Firestore 读取、2 万次写入。超出后切换到 Blaze(按量付费):每 10 万次 Firestore 读取 $0.06,每 10 万次写入 $0.18,存储 $0.026/GB 每月。那些臭名昭著的"Firebase 账单爆炸"故事,通常是在 Blaze 计划下配置有问题的客户端陷入无限读取循环——计费器不会停。在上线前务必在 GCP 项目中设置每日预算告警。

Supabase 免费层 → Pro。免费层包含 500 MB Postgres 数据库、1 GB 存储、每月 5 万活跃用户、5 GB 出口流量。Pro 是固定 $25/月加用量——8 GB 数据库、100 GB 存储、10 万 MAU、250 GB 出口流量。Pro 以上是企业定价。定价更偏向"订阅制"而非 Firebase 的纯按量计费;账单不会意外暴涨,但计划升级需要主动操作。

对于约 1 万月活用户、数据量适中的真实应用:两者在入门付费计划下都在 $25–60/月左右。曲线在此之上开始分叉——Firebase 随流量平滑扩展;Supabase 在计划边界有阶梯式变化(Pro → Team → Enterprise)。

Firebase 账单失控的问题是真实存在的。因为 Blaze 对每次操作计费,且默认没有消费上限,一个有 bug 的客户端在无限循环中反复读取,可以在一夜之间累积四位数的账单。缓解措施:在 GCP Billing 控制台设置 $5 / $20 / $100 的预算告警,编写限制每用户读取次数的 Firestore 安全规则,以及在部署全新客户端之前务必加上速率限制。Google 有时会退款处理最严重的情况,但你不能指望这一点。

第 5 步:hello-world——Firebase

5

用 Google 登录,读取一个文档

一次性配置:打开 Firebase 控制台添加项目。进入项目后:构建 → Authentication → 启用 Google。构建 → Firestore Database → 在测试模式下创建。从项目设置中复制 Web 配置对象(包含一个 apiKey 和若干 ID)。

在你的项目中:

npm i firebase

创建 src/firebase.js

import { initializeApp } from "firebase/app";
import { getAuth, GoogleAuthProvider, signInWithPopup } from "firebase/auth";
import { getFirestore, doc, getDoc } from "firebase/firestore";

const app = initializeApp({
  apiKey: "AIza...",
  authDomain: "your-project.firebaseapp.com",
  projectId: "your-project",
  // ...rest of config
});

export const auth = getAuth(app);
export const db = getFirestore(app);
export const signInWithGoogle = () =>
  signInWithPopup(auth, new GoogleAuthProvider());

export async function getProfile(uid) {
  const snap = await getDoc(doc(db, "profiles", uid));
  return snap.exists() ? snap.data() : null;
}

在你的组件中:

import { useEffect, useState } from "react";
import { auth, signInWithGoogle, getProfile } from "./firebase";
import { onAuthStateChanged } from "firebase/auth";

export default function App() {
  const [user, setUser] = useState(null);
  const [profile, setProfile] = useState(null);

  useEffect(() => onAuthStateChanged(auth, async (u) => {
    setUser(u);
    if (u) setProfile(await getProfile(u.uid));
  }), []);

  if (!user) return <button onClick={signInWithGoogle}>Sign in</button>;
  return <div>Hi {user.displayName} — profile: {JSON.stringify(profile)}</div>;
}

运行它。点击登录按钮,弹出 Google OAuth 授权窗口;授权后,页面会用你的名字向你打招呼。在 Firestore 控制台中向 profiles/<your-uid> 写入一个文档;刷新页面,数据就出现了。这就是 30 行代码里的整个捆绑包。

第 6 步:hello-world——Supabase

6

同样的事,换成 Postgres 的方式

一次性配置:打开 Supabase 控制台新建项目。等待约 90 秒让 Postgres 实例启动。在 Authentication → Providers 中启用 Google(粘贴来自 GCP 的 Google OAuth 客户端 ID 和密钥)。在 SQL Editor 中创建一个 profiles 表:

create table profiles (
  id uuid primary key references auth.users on delete cascade,
  display_name text,
  created_at timestamptz default now()
);
alter table profiles enable row level security;
create policy "own profile" on profiles for select using (auth.uid() = id);

从项目设置中复制 SUPABASE_URLSUPABASE_ANON_KEY

在你的项目中:

npm i @supabase/supabase-js

创建 src/supabase.js

import { createClient } from "@supabase/supabase-js";

export const supabase = createClient(
  "https://<PROJECT>.supabase.co",
  "eyJ...ANON_KEY"
);

export const signInWithGoogle = () =>
  supabase.auth.signInWithOAuth({ provider: "google" });

export async function getProfile(userId) {
  const { data, error } = await supabase
    .from("profiles")
    .select("*")
    .eq("id", userId)
    .single();
  return error ? null : data;
}

在你的组件中:

import { useEffect, useState } from "react";
import { supabase, signInWithGoogle, getProfile } from "./supabase";

export default function App() {
  const [user, setUser] = useState(null);
  const [profile, setProfile] = useState(null);

  useEffect(() => {
    supabase.auth.getSession().then(({ data }) => setUser(data.session?.user ?? null));
    const { data: sub } = supabase.auth.onAuthStateChange(async (_e, session) => {
      const u = session?.user ?? null;
      setUser(u);
      if (u) setProfile(await getProfile(u.id));
    });
    return () => sub.subscription.unsubscribe();
  }, []);

  if (!user) return <button onClick={signInWithGoogle}>Sign in</button>;
  return <div>Hi {user.email} — profile: {JSON.stringify(profile)}</div>;
}

运行它。流程完全相同:点击登录,Google OAuth 弹窗,页面向你打招呼。将你的 auth.users.id 作为主键插入一行到 profiles 表中;刷新,数据出现。结构与 Firebase 几乎相同,底层数据库换成了 Postgres。

第 7 步:如何在两者之间做出选择

7

覆盖大多数情况的三个简短答案

  • 现在就要选,还不知道应用会变成什么样:选 Supabase。迁出成本更低,数据模型能处理更多形态,SQL 技能也是通用的。
  • 你的应用本质上是一个实时同步产品(聊天、协作编辑器、实时游戏状态、多设备在线状态):选 Firebase。Firestore 的实时无处不在模型在这个场景下确实优于 Supabase 的按需实时。
  • 你的应用需要对数据做 JOIN、报表或分析:选 Supabase。跟 Firestore 较劲解决这个问题是好几个月的工程量;Postgres 用一行 SQL 就能搞定。

"两者都用"是罕见但合理的选择——用 Firebase Auth + Firestore 处理应用的实时侧,用 Supabase Postgres 处理关系型数据侧。大多数团队发现这样的复杂度得不偿失,最终还是选择一个。

什么时候两者都不合适

你不需要实时功能,不需要认证即服务,数据结构也很简单。一个部署在 Vercel 上、连接 Neon Postgres 和 NextAuth 的 Next.js 应用,比任何一个 BaaS 都简单得多。"五项能力合一 SDK"的前提假设是你全都需要;但实际上你往往不需要。

你已经深度使用 AWS 或 Azure。AWS Cognito + RDS + S3(或 Azure AD B2C + Cosmos + Blob)能实现同样的功能,而且使用你已有的提供商集成。引入 Firebase 或 Supabase 意味着多一家供应商的账单,以及多一个故障半径。

你无法接受两者的数据驻留位置或合规姿态。Firebase 运行在 Google Cloud 上;Supabase Cloud 运行在 AWS 上(Team 及以上版本支持多区域)。对于严格的本地部署需求,两者都不是正确答案;自托管 Supabase 或自建服务器上的 Postgres 模式(参见在服务器上安装 Postgres 和 Redis)才是。

下一步