🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付

Node.js 中介層退潮:從「前端救星」到「成本噩夢」

如果你和我一樣,是2016年前後入行的前端,一定記得那個熱血沸騰的年代。

那時候,前端圈最響亮的口號是:「Node.js是前端的後端」。我們興奮地討論BFF、大前端,仿佛看到了前端工程師的未來——不再被後端牽著鼻子走,自己掌控整個資料鏈路

我也是在那時候,第一次用Node.js搭起了BFF層。那種「前端也能寫後端」的掌控感,至今難忘。

然而最近兩年,風向變了。

很多中大廠都在悄悄「回退」Node.js中介層。有的把邏輯收歸後端,有的切到Serverless,有的乾脆砍掉整個BFF。曾經自豪的技術棧,怎麼就成了「成本中心」?

今天,想站在咱們前端的視角,聊聊這場「退潮」背後的真實故事。

一、當年我們為什麼對BFF如此著迷?

因為,我們真的受夠了

回想一下沒有BFF的日子有多痛苦:

產品經理說:「詳情頁需要展示用戶暱稱、訂單金額、商品列表。」

你打開接口文檔,發現要調三個接口:/user/info/order/detail/product/list。三個接口調完,還要自己拼資料。

// 沒有BFF時,前端要自己聚合資料
async function getOrderPage(orderId) {
  // 串行調用三個接口
  const user = await fetch(`/api/user/info?userId=123`);
  const order = await fetch(`/api/order/detail?orderId=${orderId}`);
  const products = await fetch(`/api/product/list?orderId=${orderId}`);

  // 手動拼資料
  return {
    userName: user.name,
    orderAmount: order.amount,
    productList: products
  };
}

更崩潰的是,App端要的字段和Web端不一樣。後端說:「你們前端能不能統一一下?」

你心裡一萬隻羊駝跑過:「明明是你接口設計不合理,怪我咯?」

BFF給了我們「全棧」的尊嚴

Node.js BFF的出現,像是給前端打開了一扇窗。

// BFF層:資料聚合、裁剪、適配
router.get('/web/order/detail', async (ctx) => {
  // 並行調用,性能更好
  const [user, order, products] = await Promise.all([
    fetchUser(ctx.query.userId),
    fetchOrder(ctx.query.orderId),
    fetchProducts(ctx.query.orderId)
  ]);

  // 為Web端定製返回格式
  ctx.body = {
    userInfo: { name: user.name, avatar: user.avatar },
    orderInfo: { amount: order.amount, status: order.status },
    productList: products.map(p => ({ id: p.id, name: p.name, price: p.price }))
  };
});

// 為App端返回精簡資料
router.get('/app/order/detail', async (ctx) => {
  // 同樣的資料來源,不同的返回結構
});
  • 後端繼續提供原子接口,保持他們所謂的「純潔」
  • 我們在Node層做聚合、裁剪、適配
  • 前端只調Node層,拿到的就是「剛剛好」的資料

更重要的是,不用再求後端改接口了

字段名不對?Node層改一下。缺少資料?Node層調個新接口。響應太慢?Node層加個快取。

// 後端接口字段名不合理?BFF層一鍵改寫
const user = await fetchUser(userId);
// 後端返回的是 user_name,前端要的是 userName
return { userName: user.user_name };

那種「自己說了算」的感覺,太爽了。

二、蜜月期過後,我們開始嘗到苦果

但架構是有代價的,只是這個代價,當時我們沒算清楚。

運維噩夢:第一個週末被叫起來修伺服器的滋味

我記得特別清楚,那是2019年的一個週六早上。

手機突然狂震,群裡炸了:線上訂單頁打不開。我迷迷糊糊爬起來,登入伺服器,發現Node進程掛了。重啟,又掛。再看,記憶體洩漏。

# 前端不熟悉的運維命令
top # 看CPU
free -m # 看記憶體
tail -f /var/log/nginx/error.log # 看nginx日誌
journalctl -u node-app # 看系統日誌

那天我在電腦前蹲了四個小時,查日誌、看監控、dump記憶體快照...最後發現是一個第三方SDK有bug。

作為一個前端,我擅長的是CSS佈局、組件通信、狀態管理。伺服器的負載均衡、記憶體監控、日誌收集,這些我根本不熟。

但因為是「前端負責的BFF」,出了問題,只能自己扛。

重複勞動:每個項目都在寫一樣的代碼

後來公司擴張,業務線越來越多。每條線都要BFF,因此我們建了一套又一套。

打開代碼庫,驚人的相似:

// 業務線A的BFF
router.get('/a/order/detail', async (ctx) => {
  const data = await fetchData();
  return { code: 0, data };
});

// 業務線B的BFF
router.get('/b/order/detail', async (ctx) => {
  const data = await fetchData(); // 幾乎一樣的邏輯
  return { code: 0, data };
});

// 業務線C的BFF
router.get('/c/order/detail', async (ctx) => {
  const data = await fetchData(); // 又一遍
  return { code: 0, data };
});

這種重複勞動,本質上是在浪費我們前端的價值。我們本該花時間研究組件重用、性能優化、用戶體驗,結果天天在寫重複的資料聚合代碼。

「資料對不上」的鍋,永遠是我們背

最憋屈的是扯皮的時候。

前端調BFF接口,返回的資料缺字段。產品問:誰的問題?

後端說:「我接口返回了,你自己去看。」 BFF說:「我透傳了,沒動過。」最後查出來,是後端某個服務升級,字段名改了。

// 後端悄悄改了字段名,BFF層還在用舊的
// 後端返回:{ nickname: '張三' }
// BFF層還在用:user.name
// 前端收到:undefined

但溝通成本已經花了,時間已經耽誤了,項目已經延期了。

三、殺死BFF的,不是後端,是新技術

如果說內部問題是「慢性病」,那新技術的出現,就是對BFF的「降維打擊」。

Serverless:終於不用半夜修伺服器了

我第一次接觸Serverless,是幫朋友搞一個小程序。

// 雲函數版本的BFF
exports.main = async (event, context) => {
  const { userId, orderId } = event.query;

  // 一樣的聚合邏輯
  const [user, order] = await Promise.all([
    fetchUser(userId),
    fetchOrder(orderId)
  ]);

  return { user, order };
};

不用買機器、不用配nginx、不用考慮擴縮容。寫完代碼,serverless deploy,完事。出問題了?看日誌,改代碼,再部署。全程不用碰伺服器

而且成本低得驚人。以前BFF伺服器7x24小時運行,半夜沒人訪問也在燒錢。Serverless按調用次數計費,低流量時期幾乎不花錢。

// 傳統BFF:一直運行
app.listen(3000, () => {
  console.log('server running'); // 半夜也在運行
});

// Serverless:按需啟動
exports.handler = async (event) => {
  // 有請求才執行,執行完就銷毀
  return { statusCode: 200, body: 'hello' };
};

GraphQL:讓前後端「吵架」變少了

GraphQL剛出來時,我們覺得它不就是BFF的另一種形式嗎?但用了一段時間才發現,最大的改變是:前後端終於有了一份清晰的「契約」

# 前端聲明要什麼
query {
  order(id: "123") {
    amount
    status
    user {
      name
      avatar
    }
    products {
      name
      price
    }
  }
}
// GraphQL resolver:聚合邏輯還在,但契約更清晰了
const resolvers = {
  Order: {
    user: (order) => fetchUser(order.userId),
    products: (order) => fetchProducts(order.id)
  }
};

以前調BFF接口,返回什麼全靠看代碼、靠猜。用GraphQL,前端明確聲明要哪些字段,返回的資料結構是強類型的,IDE裡還有智能提示。

後端終於「開竅」了

這幾年,後端也在變化。

// 以前:後端堅持原子接口
GET /user/123
GET /orders?userId=123
GET /products?orderId=456

// 現在:後端提供聚合接口
GET /web/profile?userId=123
// 返回:{ user: {...}, recentOrders: [...], favoriteProducts: [...] }

後端團隊也開始重視文檔、規範字段命名、保證資料契約的穩定性。前端對BFF的依賴,自然就降低了。

四、但我們真的做錯了嗎?

寫到這裡,可能會覺得BFF是一個「錯誤的選擇」。

但我想說:在那個時間點,BFF就是最好的解。

BFF解決了當時最痛的三個問題:

  1. 不用調N個接口了:一次請求,拿到所有資料
  2. 不同端可以定製資料了:Web、App、小程序各取所需
  3. 不用求後端改接口了:我們自己能改

對於我們前端來說,BFF給了我們更大的話語權和自主權。它讓我們從「切圖仔」變成了「能掌控資料鏈路的人」。

這段經歷,也讓我們學會了後端思維:快取、並發、熔斷、限流...這些知識,現在依然在用。

五、今天,我們前端該怎麼玩?

如果你問我,現在要不要學Node.js中介層,我的答案是:要學,但不是以前那種玩法。

優先擁抱Serverless

// 傳統BFF
const app = require('express')();
app.get('/api/order', async (req, res) => {
  // 業務邏輯
});
app.listen(3000);

// Serverless版本
exports.handler = async (event) => {
  // 同樣的業務邏輯
  return { statusCode: 200, body: JSON.stringify(data) };
};

除非有特殊需求,否則優先用雲函數。運維成本幾乎為零,咱們前端可以真正專注於業務邏輯。

學GraphQL,但別只會寫resolver

// 理解GraphQL的設計思想
type User {
  id: ID!
  name: String!
  orders: [Order!]!
}

type Order {
  id: ID!
  amount: Float!
  status: String!
}

Schema優先、強類型契約、按需查詢——這些思想,會讓你對「前後端協作」有更深的理解。

把BFF當「學習後端思維」的跳板

即使以後不用BFF了,那段經歷也是寶貴的。你學會了如何處理並發、如何設計快取、如何做服務熔斷、如何排查線上問題。

// 這些能力依然有用
Promise.all([fetchA(), fetchB(), fetchC()]); // 并發控制
node --inspect-brk app.js // 調試技巧

這些能力,會讓你成為「更懂後端」的前端,在協作中更有話語權。

六、寫在最後

技術的世界,沒有永恆的真理,只有不斷變化的語境。

BFF從崛起到回落,不是一個失敗的故事,而是一個成長的印記。它見證了前端從「切圖」到「全棧」的探索,也見證了架構演進的必然規律。

對於我們每個親身經歷過的人來說,重要的是:不要停留在過去的榮光裡,也不要否定曾經的探索

保持學習,保持思考,保持對新技術的好奇。

這才是我們前端最寶貴的品質。


如果你也經歷過BFF的起起落落,歡迎在評論區聊聊你的故事。


原文出處:https://juejin.cn/post/7610997952249921536


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝22  
637
🥈
我愛JS
💬3  
10
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付