用 Fastify 寫一層薄 BFF
用 Fastify 寫一層薄 BFF
BFF 這個詞聽起來很架構、很正式,但它本質上就是一層轉譯:前端要什麼結構的資料,BFF 就負責把下游打回來的東西整形成那個結構,同時擋住前端不需要知道的東西。這層不用厚,厚了只是把後端的複雜度複製一份搬過來。
為什麼是 Fastify
Fastify v5 在 2024 年 9 月正式發布,從那之後我就沒太多理由再看其他 Node.js HTTP 框架了。核心優勢很直接:schema 驅動的 request 驗證與 response 序列化,plugin 系統有完整的作用域隔離(scope isolation),效能在 Node.js 圈子裡原本就拿得出手。
最重要的是型別支援的尺度剛好。它整合 JSON Schema,你寫一份 schema,驗證、序列化、型別推斷同時都有,不用另外拉 zod 或手補 interface。對 BFF 這種不需要複雜業務邏輯的層來說,能省不少噪音。Express 那套連 body parse 都要自己接 middleware,2025 年已經很難說服人繼續用。
BFF 的邊界怎麼守
這層夾在前端和下游服務之間,對前端它是「後端」,對下游它是「前端」。上游進來的 request 一律不信任,schema 驗證在這裡做。下游的 call 透過 HMAC 保護,HTTP header 帶 X-Internal-Auth,不需要外部 PKI,內部服務共享同一把 secret 就夠。
錯誤處理和 log 都集中在這層。下游返回的 error 不要直接透傳給前端,在這裡統一包裝、log 好 request ID,trace 才接得起來。
// routes/summary.ts
import type { FastifyPluginAsync } from 'fastify'
import { createHmac } from 'node:crypto'
const schema = {
querystring: {
type: 'object',
required: ['userId'],
properties: { userId: { type: 'string', minLength: 1 } },
},
response: {
200: {
type: 'object',
properties: {
userId: { type: 'string' },
balance: { type: 'number' },
},
},
},
}
const summaryRoute: FastifyPluginAsync = async (fastify) => {
fastify.get('/summary', { schema }, async (req, reply) => {
const { userId } = req.query as { userId: string }
const ts = Date.now().toString()
const sig = createHmac('sha256', process.env.INTERNAL_SECRET!)
.update(`${ts}:${userId}`)
.digest('hex')
const data = await fetch(`${process.env.USER_SVC}/internal/summary?userId=${userId}`, {
headers: { 'X-Internal-Auth': `${ts}.${sig}` },
}).then((r) => r.json())
return data
})
}
export default summaryRoute
Schema 在這裡同時做兩件事:進來的 query 不合格直接 400,出去的 response 按 schema 序列化,省力地把不需要暴露給前端的欄位過濾掉。這是 Fastify 最省腦的地方,不用手寫 pick/omit。
厚度控制
BFF 越薄越好,但「薄」不是不做事,是只做這層該做的事:auth context 注入、下游 call 的 HMAC 簽名、request/response 的 schema 驗證、error 包裝與 log。業務邏輯不屬於這層,一旦開始在 BFF 裡寫「如果 A 服務回 X 就去問 B 服務」這種 orchestration,就是往 API gateway 走了,責任邊界要重新想清楚。
重點整理
- Fastify v5 的 schema 驅動讓驗證與序列化合而為一,BFF 層不需要額外的 validation library。
- 下游呼叫用 HMAC 簽名保護,輕量且不依賴外部 PKI,內部服務共享 secret 即可。
- 錯誤處理和 log 集中在 BFF 層,下游 error 在這裡統一包裝,不透傳給前端。
- BFF 的邊界是 schema 驗證、認證 context 注入、下游轉譯,業務 orchestration 一旦出現就該重新評估分層。
觀點 Sheng,內文 Claude 協助 · 列入 20260613 blog 翻新計劃,新漆未乾。