BFF 是什么
BFF 全称 Backend For Frontend,为前端而生的后端。这个模式是 Sam Newman 在 2015 年左右提出来的。核心思想挺直接的:不为所有前端客户端搞一个通用的后端 API,而是为每一种用户体验创建专属的后端服务。
说白了就是在前端和后端之间加一层翻译官。它知道你前端长什么样、需要什么数据、什么时候要,然后去后端帮你拼好再送回来。

为什么会有 前端BFF 这个层
回想一下几年前的一个典型场景:公司最初只有一个 PC 端网页,后端 API 全是给网页量身定做的。后来移动端来了,iOS 和 Android 两个 App 都要上。
问题跟着就来了。移动端屏幕小,展示不了 PC 端那么多信息,需要的数据量完全不是一回事。移动端还得考虑省电、省流量、省带宽——PC 端无所谓的东西到了手机上全成了问题。
功能本身也可能不一样。PC 端你可能慢慢浏览下单,移动端可能更需要扫码比价、店内导航这类场景。后端如果只有一个通用 API,要么不断膨胀满足所有客户端的差异化需求,要么变成团队间的瓶颈——改个接口要排队等排期。
BFF模式就是这时候派上用场的。
BFF 的核心原则
Sam Newman 原文里有个说法我特别认同:一个 BFF 服务应该是”one experience, one BFF”。什么意思?一段用户体验,对应一个 BFF 服务。
比如做电商:
– 桌面端是一个 BFF
– iOS App 是一个 BFF
– Android App 是一个 BFF
每个 BFF 只为它对应的前端服务,数据格式、接口数量、聚合逻辑全由前端的实际需求驱动。不会出现”Android 发了新功能,为啥 iOS 的接口也得跟着改”这种破事。
如果要再严一点,SoundCloud 的做法是 iOS 和 Android 共用一个移动端 BFF,但桌面端单独一个。问题在于如果 iOS 和 Android 的体验差异比较大,共用一个 BFF 反而容易互相拖累。
结论是按团队分更合理。如果 iOS 和 Android 是一个团队维护,共用一个 BFF 问题不大。如果是两个独立团队,最好各用各的。
BFF 到底做什么
1. 数据聚合
微服务架构下,前端一个页面展示的数据可能要跨好几个服务。拿商品详情页来说——商品信息查商品服务,库存查库存服务,价格查价格服务,推荐查推荐服务。
没有 BFF 的话,前端得发 4 个请求,自己拼数据。有了 BFF,前端只发 1 个请求,BFF 帮你并行查 4 个下游服务,拼好了再返回来。
2. 数据裁剪
同样的用户信息,桌面端的列表页需要 name、email、avatar、role,移动端的列表页只想要 name 和 avatar。BFF 可以针对性地做裁剪,不给移动端喂不需要的数据。
3. 协议转换
下游可能是 gRPC、SOAP,甚至老旧的 XML 接口,BFF 可以统一转成前端友好的 JSON REST 或 GraphQL 端点。前端根本不用关心后端用了什么协议。
4. 认证和会话管理
BFF 可以作为认证网关,统一处理登录、Token 刷新、权限校验。前端拿到的就是已经洗干净的数据。
前端BFF 和 API Gateway 有什么区别
这是问得最多的问题。放在一起看就清楚了:
| 维度 | API Gateway | BFF |
|---|---|---|
| 定位 | 通用路由网关 | 前端专用后端 |
| 受众 | 所有客户端 | 特定的前端体验 |
| 职责 | 路由、限流、认证、日志 | 数据聚合、裁剪、UI 适配 |
| 团队维护 | 基础设施团队 | 前端团队 |
| 代码规模 | 配置驱动,比较通用 | 和前端紧密耦合 |
实际生产里 API Gateway 和 BFF 往往是前后串联的关系:先过 Gateway(限流、认证),再到 BFF(聚合数据),再到下游微服务。BFF 不是要取代 Gateway,它们解决的是不同层次的问题。
Node.js 在前端BFF 中的角色
前端团队最熟悉的语言是什么?JavaScript、TypeScript。BFF 用 Node.js 写,前端团队自己维护,省去了跨团队的沟通成本。这就是为什么很多前端 BFF 实践都选了 Node.js。
常用的框架:
– Next.js API Routes / Server Actions — 如果你在用 React,这可能是最自然的 BFF 方案
– NestJS — TypeScript 全栈框架,适合更重一点的 BFF
– Express / Fastify — 轻量灵活,自己搭
举个 Express 的例子感受一下:
```javascript
// BFF 服务:合并商品详情
app.get('/api/product/:id', async (req, res) => {
const [product, stock, price] = await Promise.all([
fetch(`http://product-service/products/${req.params.id}`),
fetch(`http://inventory-service/stock/${req.params.id}`),
fetch(`http://pricing-service/price/${req.params.id}`),
]);
const data = {
...(await product.json()),
stock: (await stock.json()).quantity,
price: (await price.json()).amount,
};
// 移动端不需要那么多字段
if (req.headers['x-client'] === 'mobile') {
delete data.description;
delete data.specs;
}
res.json(data);
});
```
BFF 的常见坑
1. 变成了”胖中间层”
BFF 本意是薄薄的一层。但如果放了太多业务逻辑进去,就会退化成又一个单点瓶颈。BFF 只该做和数据呈现、端侧适配相关的逻辑,业务规则不要往里放。
2. 引入额外延迟
多加一层就多一次网络跳转。数据聚合时如果不注意并行请求,串行调用累积起来,前端感受很明显。尽量用 Promise.all 并行拉取下游数据。
3. 团队职责不清
如果 BFF 还在后端团队手里维护,那基本白搭。BFF 的精髓是由前端团队自己维护、按前端节奏发版——我为我自己的用户体验负责。如果前端改个展示字段还得排后端的队,BFF 就没意义了。
4. 过度设计
一个简单的 CRUD 管理后台,或者只有一个前端客户端,不需要 BFF。加一层没有收益,反而多了维护成本。BFF 适用于多客户端、数据来源分散的场景,不要上来就套。
什么时候该考虑 BFF
结合 Sam Newman 原文和微软 Azure 架构指南:
适合的:
– 多个前端客户端(Web、iOS、Android)需求有明显差异
– 后端是微服务架构,数据来源分散
– 前端团队想要掌控自己的 API 节奏
不适合的:
– 只有一个前端客户端
– 所有客户端需求基本一致
– 项目还在 MVP 阶段,快速验证比架构完美更重要
总结
BFF 不是一个花哨的架构模式。它的诞生是因为一个现实问题:多端共存时代,通用后端 API 已经满足不了不同前端的差异化需求。
它的价值在于把接口定义权从后端交到了前端团队手里。做 BFF 本质上不是让前端写后端代码,而是让最了解用户体验的人来决定数据怎么组织、什么时候拼好、怎么返回来。
但这层不是免费的午餐。多一层就多一次网络跳转、多一个部署维护的服务、多一份复杂度。要不要引入,什么时候引入,取决于你的实际场景,而不是架构潮流。
参考链接:
– Sam Newman — Backends For Frontends
– Microsoft Azure — BFF Pattern
📖 推荐阅读
看看这些文章,你可能会感兴趣:
React Server Components 指南:原理、实战与框架对比
浅析:Next.js 15 为什么成为 Agentic AI 应用标准?