id, title, type, domain, stack, tags, level, prerequisites, est_hours, related, week
| id |
title |
type |
domain |
stack |
tags |
level |
prerequisites |
est_hours |
related |
week |
| backend.nodejs.scenario.ai-bff |
用 Node.js 给 AI 应用做 BFF:鉴权、限流、流式转发、超时和断开处理 |
scenario |
backend |
| nodejs |
| redis |
| nginx |
| fastapi |
|
| BFF |
| SSE |
| 流式转发 |
| 限流 |
| 超时 |
| request id |
|
intermediate |
| backend.nodejs.express-nest |
|
2 |
| backend.nodejs.interview |
| frontend.ssr.scenario.help-center |
|
|
用 Node.js 给 AI 应用做 BFF:鉴权、限流、流式转发、超时和断开处理
业务背景与问题
前端团队维护的 AI 助手页面直接调用 Python 的 Agent 服务,问题:
- Agent 服务要校验各业务线不同的登录态,越来越臃肿;
- 页面需要的数据来自 3 个服务,前端串行请求,首屏慢;
- 用户关掉页面后,Agent 仍在生成,浪费模型费用;
- 出问题时无法串起前端、BFF、Agent 三方日志。
约束
- 前端团队熟悉 TypeScript,希望自己维护这一层。
- Agent 服务由 AI 团队维护,接口为 SSE。
方案设计
- BFF(Express 5 或 NestJS):统一鉴权(会话 / JWT)、按用户限流(Redis)、生成并透传
x-request-id。
- 聚合接口:首屏所需的用户信息、会话列表、配额用
Promise.allSettled 并行请求,各自设超时,部分失败时降级返回。
- 流式转发:
fetch 上游时传入 AbortSignal,res.on("close") 时中止;首字节超时和总超时分别控制;上游出错时发送 SSE error 事件而不是挂起。
- 部署:多实例无状态,Nginx 关闭对 SSE 路由的缓冲,设置较长的读超时。
关键代码
可运行的基础版本(含测试):examples/express-bff。
取舍
| 选择 |
优点 |
缺点 |
| Node BFF(本方案) |
前端团队自治、类型共享、擅长 I/O |
多一层服务和一跳网络 |
| Next.js 路由处理器做 BFF |
和页面同一个项目 |
受部署形态限制(Serverless 超时) |
| 由 Agent 服务直接对外 |
链路短 |
鉴权、聚合逻辑侵入 AI 服务 |
易错点
- 把上游的流读完再返回(
await upstream.text()),失去流式效果。
- 没有向上游传播取消。
Promise.all 中一个接口失败导致整页失败:首屏聚合用 allSettled + 降级。
- 在 BFF 里堆业务逻辑,最后变成“第二个后端”。
面试怎么讲
“AI 助手页面我们加了一层 Node BFF:统一鉴权和按用户限流,生成 request id 透传到 Agent 服务;首屏数据并行聚合、部分失败降级;对话接口把 Agent 的 SSE 原样透传,用户断开时通过 AbortController 中止上游,节省模型费用;上游失败时发 error 事件。BFF 保持薄,只做协议适配和横切逻辑。”