| name | nodejs-best-practices |
| description | Node.js 開發原則與決策。框架選擇、非同步模式、安全性與架構。教你思考而非複製。 |
| allowed-tools | Read, Write, Edit, Glob, Grep |
Node.js 最佳實踐
2025 年 Node.js 開發的原則與決策。
學習思考方式,而非記憶程式碼模式。
⚠️ 如何使用此技能
此技能教導決策原則,而非固定的程式碼複製。
- 不明確時詢問使用者偏好
- 根據情境選擇框架/模式
- 不要每次都預設相同的解決方案
1. 框架選擇(2025)
決策樹
你要建構什麼?
│
├── 邊緣/無伺服器(Cloudflare、Vercel)
│ └── Hono(零依賴、超快冷啟動)
│
├── 高效能 API
│ └── Fastify(比 Express 快 2-3 倍)
│
├── 企業/團隊熟悉度
│ └── NestJS(結構化、DI、裝飾器)
│
├── 遺留/穩定/最大生態系
│ └── Express(成熟、最多中介軟體)
│
└── 帶前端的全端
└── Next.js API Routes 或 tRPC
比較原則
| 因素 | Hono | Fastify | Express |
|---|
| 最適合 | 邊緣、無伺服器 | 效能 | 遺留、學習 |
| 冷啟動 | 最快 | 快 | 中等 |
| 生態系 | 成長中 | 好 | 最大 |
| TypeScript | 原生 | 優秀 | 好 |
| 學習曲線 | 低 | 中 | 低 |
選擇時要問的問題:
- 部署目標是什麼?
- 冷啟動時間重要嗎?
- 團隊有現有經驗嗎?
- 有遺留程式碼要維護嗎?
2. 執行環境考量(2025)
原生 TypeScript
Node.js 22+:--experimental-strip-types
├── 直接執行 .ts 檔案
├── 簡單專案不需要建構步驟
└── 適用於:腳本、簡單 API
模組系統決策
ESM(import/export)
├── 現代標準
├── 更好的 tree-shaking
├── 非同步模組載入
└── 用於:新專案
CommonJS(require)
├── 遺留相容性
├── 更多 npm 套件支援
└── 用於:現有程式碼庫、某些邊界情況
執行環境選擇
| 執行環境 | 最適合 |
|---|
| Node.js | 通用、最大生態系 |
| Bun | 效能、內建打包器 |
| Deno | 安全優先、內建 TypeScript |
3. 架構原則
分層結構概念
請求流程:
│
├── 控制器/路由層
│ ├── 處理 HTTP 細節
│ ├── 在邊界進行輸入驗證
│ └── 呼叫服務層
│
├── 服務層
│ ├── 業務邏輯
│ ├── 框架無關
│ └── 呼叫資料存取層
│
└── 資料存取層
├── 僅資料存取
├── 資料庫查詢
└── ORM 互動
為什麼重要:
- 可測試性:獨立模擬各層
- 彈性:不影響業務邏輯就能替換資料庫
- 清晰:每層都有單一職責
何時簡化:
- 小型腳本 → 單一檔案可以
- 原型 → 較少結構可接受
- 永遠問:「這會成長嗎?」
4. 錯誤處理原則
集中式錯誤處理
模式:
├── 建立自訂錯誤類別
├── 從任何層拋出
├── 在頂層捕獲(中介軟體)
└── 格式化一致的回應
錯誤回應哲學
客戶端取得:
├── 適當的 HTTP 狀態碼
├── 供程式處理的錯誤碼
├── 使用者友善的訊息
└── 無內部細節(安全性!)
日誌取得:
├── 完整堆疊追蹤
├── 請求情境
├── 使用者 ID(如適用)
└── 時間戳
狀態碼選擇
| 情況 | 狀態碼 | 時機 |
|---|
| 錯誤輸入 | 400 | 客戶端發送了無效資料 |
| 未驗證 | 401 | 缺少或無效的憑證 |
| 無權限 | 403 | 有效驗證,但不被允許 |
| 找不到 | 404 | 資源不存在 |
| 衝突 | 409 | 重複或狀態衝突 |
| 驗證錯誤 | 422 | Schema 有效但業務規則失敗 |
| 伺服器錯誤 | 500 | 我們的問題,記錄所有內容 |
5. 非同步模式原則
何時使用各種模式
| 模式 | 適用時機 |
|---|
async/await | 循序非同步操作 |
Promise.all | 平行獨立操作 |
Promise.allSettled | 平行但部分可失敗 |
Promise.race | 逾時或第一個回應優先 |
事件迴圈意識
I/O 密集型(async 有幫助):
├── 資料庫查詢
├── HTTP 請求
├── 檔案系統
└── 網路操作
CPU 密集型(async 沒幫助):
├── 加密操作
├── 圖片處理
├── 複雜計算
└── → 使用 worker threads 或卸載
避免阻塞事件迴圈
- 生產環境中絕不使用同步方法(fs.readFileSync 等)
- 卸載 CPU 密集工作
- 大資料使用串流
6. 驗證原則
在邊界驗證
在哪裡驗證:
├── API 入口點(請求 body/params)
├── 資料庫操作前
├── 外部資料(API 回應、檔案上傳)
└── 環境變數(啟動時)
驗證函式庫選擇
| 函式庫 | 最適合 |
|---|
| Zod | TypeScript 優先、型別推斷 |
| Valibot | 更小的打包(可 tree-shake) |
| ArkType | 效能關鍵 |
| Yup | 現有 React Form 使用 |
驗證哲學
- 快速失敗:儘早驗證
- 要具體:清晰的錯誤訊息
- 不要信任:即使是「內部」資料
7. 安全原則
安全檢查清單(非程式碼)
安全思維
不信任任何東西:
├── 查詢參數 → 驗證
├── 請求 body → 驗證
├── 標頭 → 驗證
├── Cookie → 驗證
├── 檔案上傳 → 掃描
└── 外部 API → 驗證回應
8. 測試原則
測試策略選擇
| 類型 | 用途 | 工具 |
|---|
| 單元 | 業務邏輯 | node:test、Vitest |
| 整合 | API 端點 | Supertest |
| E2E | 完整流程 | Playwright |
測試什麼(優先順序)
- 關鍵路徑:驗證、支付、核心業務
- 邊界情況:空輸入、邊界值
- 錯誤處理:事情失敗時會怎樣?
- 不值得測試的:框架程式碼、瑣碎的 getter
內建測試執行器(Node.js 22+)
node --test src/**/*.test.ts
├── 無外部依賴
├── 好的覆蓋率報告
└── 監看模式可用
10. 要避免的反模式
❌ 不要:
- 新的邊緣專案使用 Express(用 Hono)
- 生產程式碼中使用同步方法
- 把業務邏輯放在控制器中
- 跳過輸入驗證
- 寫死密鑰
- 不驗證就信任外部資料
- 用 CPU 工作阻塞事件迴圈
✅ 要:
- 根據情境選擇框架
- 不明確時詢問使用者偏好
- 成長中的專案使用分層架構
- 驗證所有輸入
- 密鑰使用環境變數
- 最佳化前先做效能分析
11. 決策檢查清單
實作前:
記住:Node.js 最佳實踐是關於決策,而非記憶模式。每個專案都值得根據其需求進行全新考量。