nodejs-best-practices

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Node.js Best Practices

Node.js 最佳实践

Principles and decision-making for Node.js development in 2025. Learn to THINK, not memorize code patterns.

2025年Node.js开发的原则与决策方法。 学会思考,而非死记硬背代码模式。

⚠️ How to Use This Skill

⚠️ 如何使用这份指南

This skill teaches decision-making principles, not fixed code to copy.
  • ASK user for preferences when unclear
  • Choose framework/pattern based on CONTEXT
  • Don't default to same solution every time

这份指南传授的是决策原则,而非可供复制的固定代码。
  • 当情况不明确时,询问用户的偏好
  • 根据具体场景选择框架/模式
  • 不要每次都默认使用相同的解决方案

1. Framework Selection (2025)

1. 框架选择(2025年)

Decision Tree

决策树

What are you building?
├── Edge/Serverless (Cloudflare, Vercel)
│   └── Hono (zero-dependency, ultra-fast cold starts)
├── High Performance API
│   └── Fastify (2-3x faster than Express)
├── Enterprise/Team familiarity
│   └── NestJS (structured, DI, decorators)
├── Legacy/Stable/Maximum ecosystem
│   └── Express (mature, most middleware)
└── Full-stack with frontend
    └── Next.js API Routes or tRPC
你要构建什么?
├── 边缘/无服务器场景(Cloudflare、Vercel)
│   └── Hono(零依赖,超快速冷启动)
├── 高性能API
│   └── Fastify(比Express快2-3倍)
├── 企业级/团队熟悉度优先
│   └── NestJS(结构化、依赖注入、装饰器)
├── 遗留系统/稳定/生态最完善
│   └── Express(成熟,拥有最多中间件)
└── 带前端的全栈项目
    └── Next.js API Routes 或 tRPC

Comparison Principles

对比原则

FactorHonoFastifyExpress
Best forEdge, serverlessPerformanceLegacy, learning
Cold startFastestFastModerate
EcosystemGrowingGoodLargest
TypeScriptNativeExcellentGood
Learning curveLowMediumLow
因素HonoFastifyExpress
适用场景边缘计算、无服务器高性能需求遗留系统、入门学习
冷启动速度最快中等
生态系统增长中良好最庞大
TypeScript支持原生支持优秀良好
学习曲线中等

Selection Questions to Ask:

选择时需询问的问题:

  1. What's the deployment target?
  2. Is cold start time critical?
  3. Does team have existing experience?
  4. Is there legacy code to maintain?

  1. 部署目标是什么?
  2. 冷启动时间是否关键?
  3. 团队是否有相关技术经验?
  4. 是否需要维护遗留代码?

2. Runtime Considerations (2025)

2. 运行时考量(2025年)

Native TypeScript

原生TypeScript支持

Node.js 22+: --experimental-strip-types
├── Run .ts files directly
├── No build step needed for simple projects
└── Consider for: scripts, simple APIs
Node.js 22+:--experimental-strip-types
├── 直接运行.ts文件
├── 简单项目无需构建步骤
└── 适用场景:脚本、简单API

Module System Decision

模块系统决策

ESM (import/export)
├── Modern standard
├── Better tree-shaking
├── Async module loading
└── Use for: new projects

CommonJS (require)
├── Legacy compatibility
├── More npm packages support
└── Use for: existing codebases, some edge cases
ESM(import/export)
├── 现代标准
├── 更优的Tree-shaking
├── 异步模块加载
└── 适用场景:新项目

CommonJS(require)
├── 遗留系统兼容性
├── 支持更多npm包
└── 适用场景:现有代码库、部分边缘场景

Runtime Selection

运行时选择

RuntimeBest For
Node.jsGeneral purpose, largest ecosystem
BunPerformance, built-in bundler
DenoSecurity-first, built-in TypeScript

运行时适用场景
Node.js通用场景,生态最庞大
Bun高性能需求,内置打包工具
Deno安全优先,内置TypeScript支持

3. Architecture Principles

3. 架构原则

Layered Structure Concept

分层结构概念

Request Flow:
├── Controller/Route Layer
│   ├── Handles HTTP specifics
│   ├── Input validation at boundary
│   └── Calls service layer
├── Service Layer
│   ├── Business logic
│   ├── Framework-agnostic
│   └── Calls repository layer
└── Repository Layer
    ├── Data access only
    ├── Database queries
    └── ORM interactions
请求流程:
├── 控制器/路由层
│   ├── 处理HTTP相关细节
│   ├── 在边界处做输入验证
│   └── 调用服务层
├── 服务层
│   ├── 实现业务逻辑
│   ├── 与框架无关
│   └── 调用数据访问层
└── 数据访问层
    ├── 仅负责数据访问
    ├── 数据库查询
    └── ORM交互

Why This Matters:

分层的重要性:

  • Testability: Mock layers independently
  • Flexibility: Swap database without touching business logic
  • Clarity: Each layer has single responsibility
  • 可测试性:可独立Mock各层
  • 灵活性:无需修改业务逻辑即可更换数据库
  • 清晰性:每一层都有单一职责

When to Simplify:

何时可以简化:

  • Small scripts → Single file OK
  • Prototypes → Less structure acceptable
  • Always ask: "Will this grow?"

  • 小型脚本 → 单文件即可
  • 原型项目 → 可以接受较少的结构
  • 始终要问:“这个项目会扩展吗?”

4. Error Handling Principles

4. 错误处理原则

Centralized Error Handling

集中式错误处理

Pattern:
├── Create custom error classes
├── Throw from any layer
├── Catch at top level (middleware)
└── Format consistent response
模式:
├── 创建自定义错误类
├── 在任意层抛出错误
├── 在顶层(中间件)捕获
└格式化统一响应

Error Response Philosophy

错误响应理念

Client gets:
├── Appropriate HTTP status
├── Error code for programmatic handling
├── User-friendly message
└── NO internal details (security!)

Logs get:
├── Full stack trace
├── Request context
├── User ID (if applicable)
└── Timestamp
客户端收到:
├── 合适的HTTP状态码
├── 供程序处理的错误码
├── 用户友好的提示信息
└── 不包含任何内部细节(安全考虑!)

日志记录:
├── 完整的堆栈跟踪
├── 请求上下文
├── 用户ID(如果适用)
└── 时间戳

Status Code Selection

状态码选择

SituationStatusWhen
Bad input400Client sent invalid data
No auth401Missing or invalid credentials
No permission403Valid auth, but not allowed
Not found404Resource doesn't exist
Conflict409Duplicate or state conflict
Validation422Schema valid but business rules fail
Server error500Our fault, log everything

场景状态码适用时机
无效输入400客户端发送了无效数据
未授权401缺少或无效的凭证
无权限403凭证有效,但无操作权限
资源不存在404请求的资源不存在
冲突409重复数据或状态冲突
验证失败422schema合法但违反业务规则
服务器错误500服务端故障,需记录所有信息

5. Async Patterns Principles

5. 异步模式原则

When to Use Each

各模式的适用场景

PatternUse When
async/await
Sequential async operations
Promise.all
Parallel independent operations
Promise.allSettled
Parallel where some can fail
Promise.race
Timeout or first response wins
模式适用时机
async/await
顺序执行的异步操作
Promise.all
并行执行的独立操作
Promise.allSettled
并行执行且允许部分失败的操作
Promise.race
超时或取第一个响应的场景

Event Loop Awareness

事件循环认知

I/O-bound (async helps):
├── Database queries
├── HTTP requests
├── File system
└── Network operations

CPU-bound (async doesn't help):
├── Crypto operations
├── Image processing
├── Complex calculations
└── → Use worker threads or offload
I/O密集型(异步有帮助):
├── 数据库查询
├── HTTP请求
├── 文件系统操作
└── 网络操作

CPU密集型(异步无帮助):
├── 加密操作
├── 图片处理
├── 复杂计算
└── → 使用Worker Threads或卸载到外部服务

Avoiding Event Loop Blocking

避免阻塞事件循环

  • Never use sync methods in production (fs.readFileSync, etc.)
  • Offload CPU-intensive work
  • Use streaming for large data

  • 生产环境中绝不使用同步方法(如fs.readFileSync等)
  • 将CPU密集型任务卸载
  • 对大数据使用流式处理

6. Validation Principles

6. 验证原则

Validate at Boundaries

在边界处验证

Where to validate:
├── API entry point (request body/params)
├── Before database operations
├── External data (API responses, file uploads)
└── Environment variables (startup)
验证位置:
├── API入口(请求体/参数)
├── 数据库操作前
├── 外部数据(API响应、文件上传)
└── 环境变量(启动时)

Validation Library Selection

验证库选择

LibraryBest For
ZodTypeScript first, inference
ValibotSmaller bundle (tree-shakeable)
ArkTypePerformance critical
YupExisting React Form usage
适用场景
ZodTypeScript优先,支持类型推断
Valibot更小的打包体积(支持Tree-shaking)
ArkType性能优先场景
Yup已有React Form使用场景

Validation Philosophy

验证理念

  • Fail fast: Validate early
  • Be specific: Clear error messages
  • Don't trust: Even "internal" data

  • 快速失败:尽早验证
  • 明确具体:清晰的错误提示
  • 绝不信任:即使是“内部”数据也需验证

7. Security Principles

7. 安全原则

Security Checklist (Not Code)

安全检查清单(非代码)

  • Input validation: All inputs validated
  • Parameterized queries: No string concatenation for SQL
  • Password hashing: bcrypt or argon2
  • JWT verification: Always verify signature and expiry
  • Rate limiting: Protect from abuse
  • Security headers: Helmet.js or equivalent
  • HTTPS: Everywhere in production
  • CORS: Properly configured
  • Secrets: Environment variables only
  • Dependencies: Regularly audited
  • 输入验证:所有输入都经过验证
  • 参数化查询:SQL查询不使用字符串拼接
  • 密码哈希:使用bcrypt或argon2
  • JWT验证:始终验证签名和过期时间
  • 速率限制:防止滥用
  • 安全头:使用Helmet.js或同类工具
  • HTTPS:生产环境全程使用
  • CORS:正确配置
  • 密钥:仅通过环境变量管理
  • 依赖:定期审计

Security Mindset

安全思维

Trust nothing:
├── Query params → validate
├── Request body → validate
├── Headers → verify
├── Cookies → validate
├── File uploads → scan
└── External APIs → validate response

绝不信任任何内容:
├── 查询参数 → 验证
├── 请求体 → 验证
├── 请求头 → 验证
├── Cookie → 验证
├── 文件上传 → 扫描
└── 外部API → 验证响应

8. Testing Principles

8. 测试原则

Test Strategy Selection

测试策略选择

TypePurposeTools
UnitBusiness logicnode:test, Vitest
IntegrationAPI endpointsSupertest
E2EFull flowsPlaywright
类型目的工具
单元测试测试业务逻辑node:test, Vitest
集成测试测试API端点Supertest
端到端测试测试完整流程Playwright

What to Test (Priorities)

测试优先级

  1. Critical paths: Auth, payments, core business
  2. Edge cases: Empty inputs, boundaries
  3. Error handling: What happens when things fail?
  4. Not worth testing: Framework code, trivial getters
  1. 关键路径:认证、支付、核心业务逻辑
  2. 边缘场景:空输入、边界值
  3. 错误处理:故障发生时的表现
  4. 无需测试的内容:框架代码、简单的getter方法

Built-in Test Runner (Node.js 22+)

内置测试运行器(Node.js 22+)

node --test src/**/*.test.ts
├── No external dependency
├── Good coverage reporting
└── Watch mode available

node --test src/**/*.test.ts
├── 无外部依赖
├── 支持覆盖率报告
└── 支持监听模式

10. Anti-Patterns to Avoid

10. 需避免的反模式

❌ DON'T:

❌ 不要:

  • Use Express for new edge projects (use Hono)
  • Use sync methods in production code
  • Put business logic in controllers
  • Skip input validation
  • Hardcode secrets
  • Trust external data without validation
  • Block event loop with CPU work
  • 新项目使用Express开发边缘场景(改用Hono)
  • 生产代码中使用同步方法
  • 将业务逻辑放在控制器中
  • 跳过输入验证
  • 硬编码密钥
  • 未验证就信任外部数据
  • 用CPU密集型任务阻塞事件循环

✅ DO:

✅ 应该:

  • Choose framework based on context
  • Ask user for preferences when unclear
  • Use layered architecture for growing projects
  • Validate all inputs
  • Use environment variables for secrets
  • Profile before optimizing

  • 根据场景选择框架
  • 情况不明确时询问用户偏好
  • 对会扩展的项目使用分层架构
  • 验证所有输入
  • 使用环境变量管理密钥
  • 先分析再优化

11. Decision Checklist

11. 决策检查清单

Before implementing:
  • Asked user about stack preference?
  • Chosen framework for THIS context? (not just default)
  • Considered deployment target?
  • Planned error handling strategy?
  • Identified validation points?
  • Considered security requirements?

Remember: Node.js best practices are about decision-making, not memorizing patterns. Every project deserves fresh consideration based on its requirements.
实现前需确认:
  • 是否询问过用户的技术栈偏好?
  • 是否针对当前场景选择了合适的框架?(而非默认框架)
  • 是否考虑了部署目标?
  • 是否规划了错误处理策略?
  • 是否确定了验证点?
  • 是否考虑了安全需求?

记住:Node.js最佳实践的核心是决策方法,而非死记硬背模式。每个项目都应根据自身需求重新评估。