先用 Node.js stdlib 寫一個 API

Node.js 的 http module 可以直接建 HTTP server,不需要任何框架:

const http = require('http');
 
const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify({ message: 'hello' }));
});
 
server.listen(3000);

這能跑。那如果要做一個 GET /users/:id 的 API 呢?

const server = http.createServer((req, res) => {
  const url = new URL(req.url, 'http://localhost');
  const match = url.pathname.match(/^\/users\/(\d+)$/);
 
  if (req.method === 'GET' && match) {
    const id = match[1];
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ id, name: 'Alice' }));
    return;
  }
 
  res.writeHead(404);
  res.end('Not Found');
});

你已經在寫路由了——只是用正規表達式手動寫。

繼續加功能。要解析 POST body:

const server = http.createServer((req, res) => {
  if (req.method === 'POST' && req.url === '/users') {
    let body = '';
    req.on('data', chunk => { body += chunk.toString(); });
    req.on('end', () => {
      const data = JSON.parse(body);
      // 處理 data...
      res.writeHead(201);
      res.end(JSON.stringify(data));
    });
    return;
  }
  // ...
});

你在寫 body parser——只是手動用 stream 讀。

再加 auth 檢查:

const server = http.createServer((req, res) => {
  // 每個受保護的 route 都要重複這段
  const token = req.headers['authorization']?.split(' ')[1];
  if (!token || !verifyToken(token)) {
    res.writeHead(401);
    res.end('Unauthorized');
    return;
  }
  // 真正的邏輯...
});

你在寫 middleware——只是每個 handler 都複製貼上這段。


你重複發明了什麼

用 stdlib 寫到這裡,你已經實作了:

  • 路由器(URL pattern 匹配 + method 分流)
  • Body parser(HTTP stream → parsed object)
  • Middleware chain(在 handler 前跑 auth、log 等邏輯)

這些不是你的商業邏輯,這些是每個 HTTP server 都需要的基礎設施。而且你的實作:

  • 路由用正規表達式,遇到複雜 URL pattern 很快就爆炸
  • Body parser 沒有處理 content-type 多樣性(JSON / form-data / multipart)
  • Middleware 是手動複製貼上,不是一條可組合的鏈

Framework 把這些基礎設施做好,讓你只寫商業邏輯。


沒有 framework 在大一點的專案撞牆

問題一:路由維護

25 個 route 之後,你的 createServer callback 會變成一個巨大的 if-else 樹。你可以把它拆成多個函式,但你得自己設計拆法、維護 convention——新人進來要先搞懂你的路由邏輯是怎麼組織的。

Express 的解法:express.Router(),路由可以模組化、可以巢狀、可以 middleware 隔離,不需要你設計這套東西。

問題二:中介層的順序依賴

你的 auth middleware 需要先解析 body(才能從 body 拿 token),但 CORS preflight 需要在 auth 之前回應。手寫的 if-else 路由很難清楚表達「這個 middleware 只在這些 routes 跑、那個 middleware 跑在所有 routes 之前」。

Framework 的 middleware chain 讓你聲明性地定義順序和作用範圍。

問題三:錯誤處理的一致性

stdlib 的 try-catch 要每個 handler 各自處理。你忘了一個地方,那個 handler 拋出的 unhandled exception 就讓 server crash 或掛起。

Express 的 4-param error middleware、NestJS 的 ExceptionFilter、Spring 的 @ControllerAdvice——都是為了讓你在一個地方處理所有的錯誤,格式統一,不遺漏。


什麼時候不用 framework 是對的

不是說 stdlib 就是錯的。有幾個場景 stdlib 確實是對的選擇:

Serverless 函式:如果你的邏輯就是「收一個 event、處理、回傳」,沒有複雜路由,stdlib 夠用,加 framework 反而增加 cold start 時間。

學習目的:自己用 stdlib 寫一次完整的 HTTP server,你才能真正理解 framework 在幫你省什麼。W04 walkthrough 就是做這件事——先不用 framework 感受一次痛,再用 framework 感受它解了什麼。

極致效能場景:framework 的抽象有 overhead。如果你的 API 需要每秒百萬請求,可能要從 stdlib 或極精簡的 framework 出發,但這個等級的需求在現實中很稀少。


Python 的版本

Python 的情況類似。用 http.serverasyncio + aiohttp 的原生 API 可以寫 HTTP server,但路由、validation、session 管理都要自己來。

Django 和 FastAPI 解決的問題集是一樣的——只是 FastAPI 解法更 explicit(你清楚地看到每層在做什麼),Django 解法更 magical(大量行為被 convention 和 class 繼承管理了,你看不太到)。

Python Framework 比較 有具體的對比。


接下來

知道「為什麼需要 framework」之後,下一個問題是Web Framework 演進史——為什麼每個語言會演化出多個 framework、每一代在解前一代的什麼極限。