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)
├── 现代标准
├── 更优的树摇优化
├── 异步模块加载
└── 适用场景:新项目

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
  • 可测试性:可独立模拟各层
  • 灵活性:无需修改业务逻辑即可更换数据库
  • 清晰性:每一层都有单一职责

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密集型(异步无帮助):
├── 加密操作
├── 图像处理
├── 复杂计算
└── → 使用工作线程或卸载任务

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更小的打包体积(支持树摇)
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最佳实践的核心是决策制定,而非死记硬背模式。每个项目都应根据自身需求重新考量。",