SKILL.md
readonlyread-only
name
nodejs-best-practices
description
Node.js 開發原則與決策方法。框架選擇、非同步模式、安全性與架構。教你思考,而非複製。
Node.js 最佳實務
2025 年 Node.js 開發的原則與決策方法。
學會思考,而非死記程式碼模式。
使用時機
當你需要進行 Node.js 架構決策、選擇框架、設計非同步模式,或應用安全性與部署最佳實務時,使用此技能。
⚠️ 如何使用此技能
此技能教導決策原則,而非固定的程式碼複製。
- 若不清楚使用者偏好,請主動詢問
- 根據情境選擇框架/模式
- 不要每次都預設使用同一種解決方案
1. 框架選擇(2025)
決策樹
你在建置什麼?
│
├── Edge/Serverless(Cloudflare、Vercel)
│ └── Hono(零依賴、超快冷啟動)
│
├── 高效能 API
│ └── Fastify(比 Express 快 2-3 倍)
│
├── 企業級/團隊熟悉度
│ └── NestJS(結構化、依賴注入、裝飾器)
│
├── 既有/穩定/最大生態系
│ └── Express(成熟、最多中介軟體)
│
└── 前後端全端
└── Next.js API Routes 或 tRPC
比較原則
| 因素 | Hono | Fastify | Express |
|---|---|---|---|
| 最佳用途 | Edge、Serverless | 效能 | 既有系統、學習 |
| 冷啟動 | 最快 | 快 | 中等 |
| 生態系 | 成長中 | 良好 | 最大 |
| 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 密集型(非同步有幫助):
├── 資料庫查詢
├── HTTP 請求
├── 檔案系統
└── 網路操作
CPU 密集型(非同步無幫助):
├── 加密操作
├── 圖片處理
├── 複雜計算
└── → 使用 worker threads 或卸載
避免阻塞事件循環
- 生產環境絕不使用同步方法(如 fs.readFileSync)
- 卸載 CPU 密集型工作
- 處理大量資料時使用串流
6. 驗證原則
在邊界驗證
驗證位置:
├── API 進入點(請求主體/參數)
├── 資料庫操作前
├── 外部資料(API 回應、檔案上傳)
└── 環境變數(啟動時)
驗證函式庫選擇
| 函式庫 | 最佳用途 |
|---|---|
| Zod | TypeScript 優先、型別推論 |
| Valibot | 較小套件(可 tree-shake) |
| ArkType | 效能關鍵 |
| Yup | 既有 React Form 使用 |
驗證哲學
- 快速失敗:及早驗證
- 明確具體:清晰的錯誤訊息
- 不信任:即使是「內部」資料
7. 安全性原則
安全檢查清單(非程式碼)
- [ ] 輸入驗證:所有輸入皆驗證
- [ ] 參數化查詢:SQL 不使用字串串接
- [ ] 密碼雜湊:使用 bcrypt 或 argon2
- [ ] JWT 驗證:務必驗證簽章與過期時間
- [ ] 速率限制:防止濫用
- [ ] 安全標頭:使用 Helmet.js 或同等方案
- [ ] HTTPS:生產環境全面啟用
- [ ] CORS:正確設定
- [ ] 機密資訊:僅使用環境變數
- [ ] 相依套件:定期稽核
安全心態
不信任任何事物:
├── 查詢參數 → 驗證
├── 請求主體 → 驗證
├── 標頭 → 驗證
├── Cookie → 驗證
├── 檔案上傳 → 掃描
└── 外部 API → 驗證回應
8. 測試原則
測試策略選擇
| 類型 | 目的 | 工具 |
|---|---|---|
| 單元測試 | 商業邏輯 | node:test、Vitest |
| 整合測試 | API 端點 | Supertest |
| 端到端測試 | 完整流程 | Playwright |
測試重點(優先順序)
- 關鍵路徑:認證、付款、核心商業邏輯
- 邊界情況:空輸入、邊界值
- 錯誤處理:失敗時會發生什麼?
- 不值得測試:框架程式碼、瑣碎的 getter
內建測試執行器(Node.js 22+)
node --test src/**/*.test.ts
├── 無需外部相依
├── 良好的涵蓋率報告
└── 支援監看模式
9. 應避免的反模式
❌ 不要:
- 在新的 Edge 專案使用 Express(請用 Hono)
- 在生產程式碼使用同步方法
- 將商業邏輯放在控制器中
- 跳過輸入驗證
- 硬編碼機密資訊
- 不經驗證就信任外部資料
- 用 CPU 工作阻塞事件循環
✅ 要:
- 根據情境選擇框架
- 若不清楚使用者偏好,請主動詢問
- 成長中的專案使用分層架構
- 驗證所有輸入
- 使用環境變數儲存機密資訊
- 先分析再最佳化
10. 決策檢查清單
實作前:
- [ ] 是否詢問使用者技術棧偏好?
- [ ] 是否根據此情境選擇框架?(而非預設)
- [ ] 是否考慮部署目標?
- [ ] 是否規劃錯誤處理策略?
- [ ] 是否識別驗證點?
- [ ] 是否考慮安全需求?
記住:Node.js 最佳實務關乎決策,而非死記模式。每個專案都應根據其需求重新審視。
限制
- 僅在任務明確符合上述範圍時使用此技能。
- 請勿將輸出視為環境特定驗證、測試或專家審查的替代品。
- 若缺少必要輸入、權限、安全邊界或成功標準,請停止並要求釐清。






