先用 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.server 或 asyncio + aiohttp 的原生 API 可以寫 HTTP server,但路由、validation、session 管理都要自己來。
Django 和 FastAPI 解決的問題集是一樣的——只是 FastAPI 解法更 explicit(你清楚地看到每層在做什麼),Django 解法更 magical(大量行為被 convention 和 class 繼承管理了,你看不太到)。
Python Framework 比較 有具體的對比。
接下來
知道「為什麼需要 framework」之後,下一個問題是Web Framework 演進史——為什麼每個語言會演化出多個 framework、每一代在解前一代的什麼極限。
