如果你只是想给微信小程序找一个“能存数据的地方”,Supabase 当然不是唯一选择,也未必是门槛最低的选择。
你完全可以用微信云开发,也可以自己搭一套 Node.js + PostgreSQL,或者找一个更偏 BaaS 的服务先把第一版跑起来。很多项目在最开始的时候,对后端的要求其实很朴素:能登录、能存表单、能传图片、能做一点权限控制,差不多就够了。
所以“为什么选 Supabase”这个问题,真正要回答的从来不是“Supabase 有哪些功能”,而是另外一句话:
当我要在有限时间里交付一个真正能上线的小程序时,我最不想把时间花在哪里?
我的答案一直很明确:我不想把大量时间花在重复建设那些已经高度标准化的基础能力上。
我不想为每一个列表、每一个详情、每一个筛选条件都单独写一层 CRUD 接口;不想为了用户系统从零铺一遍 session、JWT、刷新、找回密码、权限隔离;也不想在项目刚起步时,就把文件上传、实时同步、数据库迁移和后台管理都拆成几个彼此独立的基础设施问题。
Supabase 吸引我的地方,不是它神奇地“替代后端”,而是它把一组原本分散的后端基础能力收在了同一个可演进的模型里:数据库是 PostgreSQL,API 来自 PostgREST,认证是基于 JWT 的 Auth,文件走 Storage,实时能力来自 Realtime,服务端扩展用 Edge Functions。它不是一个玩具级的快速方案,而是一个愿意把数据库能力还给你的交付型底座。
这组文章会怎么读
这一篇是系列导言。后面我会把“Supabase + 微信小程序”拆成 6 个主题,按实际落地顺序写清楚:
- 01:小程序怎么接入(SDK 为什么跑不起来、适配包怎么选、封装层怎么搭)
/blogs/supabase/01-miniprogram-supabase-adapter - 02:认证怎么做(微信登录、JWT 生命周期、并发刷新、静默重登)
/blogs/supabase/02-authentication-jwt-wechat-login - 03:RLS 怎么写(模板、调试方法、性能注意点)
/blogs/supabase/03-row-level-security-deep-dive - 04:数据建模与 Functions(表怎么画、Trigger/RPC/Edge Function 怎么分工)
/blogs/supabase/04-data-modeling-and-functions - 05:Storage 与 Realtime(文件路径/权限、Realtime 在小程序的现实边界与替代方案)
/blogs/supabase/05-realtime-and-storage - 06:工程实践(Migration、上线 checklist、回滚与合规)
/blogs/supabase/06-engineering-practices
如果你是第一次做这套东西,我建议先按 01 → 02 → 03 跑通最小闭环,跑通之后再回头看 04/05/06,会顺很多。
最短闭环:先跑通,再讲道理
零基础读者最容易卡在“概念都懂了,但不知道从哪一步开始”。我建议用一个最短闭环作为起点:只完成三件事——能登录、能写入一行数据、能读回自己的数据。
- 创建 Supabase 项目,记下
SUPABASE_URL与anon key - 建两张最小表:
profiles(用户资料)、posts(业务数据),并开启 RLS(03 会展开) - 部署一个
wechat-loginEdge Function:用wx.login()的code换 openid,然后为 Supabase 用户签发 session(02 会展开) - 小程序端安装
supabase-wechat-stable-v2并构建 npm,初始化createClient(01 会展开) - 登录成功后,用
supabase.from('posts').insert(...)插入一行,再用select()查回来
这 5 步跑通,你对 Supabase 的理解会从“看过介绍”变成“我已经把链路跑起来了”。后面再补 Storage、Realtime、函数与工程化,都是在这个闭环之上叠能力。
信任边界:在小程序里先把安全模型讲清楚
小程序客户端天然不可信,安全边界不能建立在“前端没提供入口”这种东西上。Supabase 的安全模型也不是“前端直连数据库也没事”,而是下面这条边界链条:
- 小程序端一定会包含
anon key,它只用于标识项目,不代表权限 - 登录后小程序持有 JWT(
access_token),它决定请求以authenticated角色执行 - 真正能读写哪些行,由 PostgreSQL 的 RLS 在数据库层强制执行
- 任何需要使用
service_role的操作,只能发生在 Edge Function / 自建服务端里,且要在服务端验证调用者身份
如果你没打算把 RLS 写清楚,那我不建议在小程序里用 Supabase 走“客户端直连”这条路。
我看后端选型,不先看功能表,先看代价
很多技术选型讨论都喜欢列横向对比:支持什么、免费额度多少、有没有 SDK、有没有管理后台。那些信息当然有用,但它们很少是决定性的。
真实项目里更关键的是成本结构。
比如一个小程序项目,如果我走传统自建后端,大概率会有这些工作:
- 设计数据库表结构
- 写用户体系和登录态
- 为每个核心业务实体写 CRUD 接口
- 处理分页、筛选、排序、关联查询
- 写权限校验和角色判断
- 处理文件上传和访问控制
- 设计部署流程、环境变量和迁移策略
这些事情里,真正有业务独特性的部分其实没那么多。大量工作只是为了让“一个正常后端”成立。
如果项目体量很大、规则很复杂,自己做这些没问题,甚至更合理。但如果项目还处在 0 到 1 阶段,或者团队本来就是前端主导,这种基础设施重复建设会非常消耗交付节奏。
Supabase 的价值就在这里。它不是把复杂性消灭了,而是把一部分复杂性前置产品化了。
它让你不用为“数据库怎么暴露 API”重新造轮子,不用为“权限应该放哪一层”重新发明一套抽象,也不用为“文件和实时能力以后怎么补”先埋一堆架构伏笔。
这对小团队尤其重要。因为小团队最缺的通常不是技术方向,而是连续的交付时间。
它真正的底座不是 Auth,也不是 Storage,而是 PostgreSQL
如果只从宣传页看 Supabase,很容易误以为它最核心的是 Auth、Realtime、Storage 这些“功能模块”。但在我看来,Supabase 真正站得住的地方,还是它没有试图绕开 PostgreSQL,而是把所有能力建在 PostgreSQL 上面。
这点非常关键。
很多 BaaS 服务早期会给你一种“特别快”的感觉,因为它们用抽象化得更彻底的方式把数据层包起来了。你不用关心表、约束、事务、索引,只要把数据塞进去,接口就能跑。问题是,一旦业务开始长出关系结构,这种抽象就会反过来限制你。
小程序项目看起来轻,但业务复杂度并不总是轻。你很快会遇到这些东西:
- 用户和资料表不是一回事
- 订单和订单项是一对多
- 内容有状态流转,不只是一个布尔字段
- 某些列表依赖多表筛选
- 文件和业务实体有归属关系
- 某些操作必须是事务性的
这时候,关系型数据库的表达能力就不是“高级功能”,而是基本盘。
Supabase 没有把这些能力藏起来,反而鼓励你直接用:外键、唯一约束、索引、视图、函数、触发器、RLS、JSONB,都是原生的。也就是说,它快的地方不是靠牺牲数据库能力换来的。
这一点让我很放心。因为我知道项目早期可以先用最简单的表和查询跑起来,但后面如果业务长复杂了,我不用因为平台表达力不够再整体迁移一次。
PostgREST 替我省掉的,不只是“写接口时间”
很多人第一次接触 Supabase,会把焦点放在一句话上:前端可以直接查数据库。
这句话本身没错,但也很容易让人误解。真正值得关注的,不是“可以少写几个接口”,而是 PostgREST 改变了接口层的成本结构。
传统后端模式下,一个很普通的列表接口都可能要经历这些步骤:
- 建表
- 写查询逻辑
- 写分页和过滤
- 写 DTO
- 写权限判断
- 写 Controller
- 写接口文档
- 前端对接并继续修字段不一致的问题
Supabase 这条链路被折叠了不少。表结构建好、RLS 策略写对、索引补齐之后,很多“数据搬运型接口”其实已经存在了。
例如一个只读列表:
const { data, error } = await supabase
.from('projects')
.select('id, title, status, created_at')
.eq('owner_id', userId)
.order('created_at', { ascending: false })
.range(0, 19)
这段代码背后并不神秘。from().select().eq().order().range() 最终都会被翻译成 PostgREST 协议层的 HTTP 请求,再映射为 SQL 查询。它没有让查询变得“无成本”,只是把原本需要在后端重复书写的一层通用接口逻辑产品化了。
我为什么喜欢这个模型?因为它很诚实。
它没有说“后端没了”,只是明确告诉你:那些结构清楚、权限可表达、没有太多领域逻辑的查询,本来就不值得每个项目都重新写一遍。
这对前端工程师尤其友好。不是因为前端终于“不需要后端了”,而是因为前端可以把精力更直接地用在真正有业务区别的部分:页面状态、交互流、字段约束、异常处理、用户路径,而不是 endlessly 写薄薄一层的数据搬运接口。
在小程序里,RLS 不是加分项,是前提
如果你问我 Supabase 在小程序场景里最重要的能力是什么,我不会先说 Auth,也不会先说 Storage,而是会先说 RLS。
原因很现实:小程序客户端天然不可信。
不管你代码写得多整洁,也不管你有没有把关键按钮藏起来,客户端最终都只是客户端。用户可以抓包,可以逆向,可以伪造请求。你不能把任何真正的安全边界建立在“小程序页面上没有提供这个入口”这种事情上。
而 Supabase 的一个核心前提恰恰是:客户端一定会拿到 anon key。如果没有 RLS,这件事几乎等于主动把数据库入口打开。
所以我一直觉得,理解 Supabase 必须先理解它的安全模型。
它的逻辑不是“给前端一个神奇 SDK,然后希望大家别乱用”,而是:
- 前端拿
anon key连接项目 - 未登录时,请求以
anon角色执行 - 登录后,请求携带 JWT,以
authenticated角色执行 - 真正能访问哪些行,由数据库策略决定
也就是说,权限不是由某个 Node.js 中间件顺手判断一下,而是在 PostgreSQL 层被强制执行。
例如:
create policy "users can read own projects"
on projects for select
using (auth.uid() = owner_id);
这条策略的意义不是“写起来方便”,而是它让越权读取这件事变成数据库层面的不可能,而不是应用层面的“尽量别忘”。
这对小程序太重要了。因为小程序项目里最容易被低估的,不是 CRUD,而是权限。
Supabase Auth 在微信小程序里并不现成,但一旦接进去,后面会很顺
我不太喜欢那种“Supabase 小程序接入超简单”的说法。因为它只在一半意义上成立。
成立的那一半是:Supabase 已经有完整的 Auth 体系,access token、refresh token、用户模型、JWT claims、会话续期这些基础问题你不用从零发明。
不成立的另一半是:微信登录不是它原生支持的第一类场景,你一定要自己做适配。
微信小程序登录的核心流程决定了这一点。客户端只能拿到 wx.login() 返回的临时 code,真正换取 openid / session_key 的动作必须在服务端完成,因为那里要用到 AppSecret。也就是说,微信身份和 Supabase Auth 用户之间需要一层映射。
这层映射并不轻松,但它做完之后的收益很大。
因为一旦你把微信用户稳定地接进 Supabase Auth,后面的事情会统一起来:
- RLS 可以直接使用
auth.uid() - refresh token 续期有现成模型
- 登录态和权限系统不再是两张皮
- 后续接别的身份方式也不会推翻现有设计
很多人看这一步,只看到“为什么这么麻烦”。我的感受正好相反:正因为这一步麻烦,所以做对了之后后面很省心。
Storage 和 Realtime 的意义,不在于“我现在就一定要用”,而在于产品不会卡死在下一步
很多项目第一版并不需要实时协作,也不一定需要复杂文件系统。最开始的诉求可能只是:用户传几张图片,列表能展示,详情能查看,仅此而已。
但项目一旦继续走,文件和实时能力几乎总会找上门来。
文件侧的问题不只是“能上传”,还包括:
- 路径如何设计,才能避免命名冲突
- 文件和业务实体之间如何建立归属关系
- 私有文件怎么访问
- 删除业务数据时是否要清理对象存储
- 公开 URL 和 signed URL 分别适合什么场景
实时侧的问题则更麻烦:
- WebSocket 如何鉴权
- 断线重连策略怎么做
- 多个频道如何隔离
- 消息顺序和幂等如何处理
- 弱网和后台切换时如何恢复
自己搭并非做不到,但对多数 0 到 1 项目来说,这不是最值得优先投入时间的地方。
Supabase 的 Storage 和 Realtime 不会替你解决所有这些细节,尤其在小程序环境里,你依然要面对 wx.uploadFile、域名白名单、Socket 适配和弱网重连。但至少它给你的是一套连续的能力,而不是要求你在项目中途再把基础设施拐一个大弯。
什么时候我不会选 Supabase
Supabase 并不是所有小程序项目的默认答案。我通常会在这些情况下更谨慎,甚至直接不选它:
- 项目明确要求私有化部署、内网部署,且团队已经有成熟的后端与运维体系
- 领域逻辑非常重,绝大多数请求都不是“数据搬运”,而是复杂规则引擎/工作流/对接企业系统
- 团队完全不愿意在数据库层写权限策略,只希望在应用层做鉴权
Supabase 的优势建立在一个前提上:你愿意把一部分“后端基础设施”交给它,但同时也愿意认真对待数据模型与 RLS。接受这个前提,收益很明显;不接受,代价会以别的方式出现。
我最后的选择标准
我最终选择的不是“最快方案”,而是“交付速度和后续演进之间比较平衡的方案”。
它确实有成本:你要理解 PostgreSQL 的基本概念;你要把 RLS 写清楚;你要处理小程序环境和官方 SDK 的不兼容;你要接受不是所有业务逻辑都适合直接走 PostgREST。
但它也带来明确收益:通用接口层不需要重复建;权限可以下沉到数据库;文件、函数、迁移这些能力是连续的;项目从 0 到 1 的节奏更容易保持。
如果你问我一句最直白的话:Supabase 适合什么样的小程序?
我的回答是:
它特别适合那些想尽快交付第一版,但又知道自己不是在写一次性 demo 的项目。
你希望快,但不想快到后面推倒重来;你希望简单,但不想简单到权限和数据模型都没法收住;你希望前端能多承担一些后端工作,但又不想为此自己从零搭一整套基础设施。
在这个区间里,Supabase 是一个很有竞争力的答案。