nodejs-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseNode.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 或 tRPCComparison Principles
对比原则
| Factor | Hono | Fastify | Express |
|---|---|---|---|
| Best for | Edge, serverless | Performance | Legacy, learning |
| Cold start | Fastest | Fast | Moderate |
| Ecosystem | Growing | Good | Largest |
| TypeScript | Native | Excellent | Good |
| Learning curve | Low | Medium | Low |
| 因素 | Hono | Fastify | Express |
|---|---|---|---|
| 最佳适用场景 | 边缘、无服务器 | 高性能 | 遗留系统、学习入门 |
| 冷启动速度 | 最快 | 快 | 中等 |
| 生态系统 | 增长中 | 良好 | 最庞大 |
| TypeScript支持 | 原生支持 | 优秀 | 良好 |
| 学习曲线 | 低 | 中等 | 低 |
Selection Questions to Ask:
选型时需询问的问题:
- What's the deployment target?
- Is cold start time critical?
- Does team have existing experience?
- Is there legacy code to maintain?
- 部署目标是什么?
- 冷启动时间是否关键?
- 团队是否有相关现有经验?
- 是否需要维护遗留代码?
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 APIsNode.js 22+:--experimental-strip-types
├── 直接运行.ts文件
├── 简单项目无需构建步骤
└── 适用场景:脚本、简单APIModule 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 casesESM(import/export)
├── 现代标准
├── 更优的树摇优化
├── 异步模块加载
└── 适用场景:新项目
CommonJS(require)
├── 遗留系统兼容性
├── 支持更多npm包
└── 适用场景:现有代码库、部分边缘场景Runtime Selection
运行时选择
| Runtime | Best For |
|---|---|
| Node.js | General purpose, largest ecosystem |
| Bun | Performance, built-in bundler |
| Deno | Security-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
状态码选择
| Situation | Status | When |
|---|---|---|
| Bad input | 400 | Client sent invalid data |
| No auth | 401 | Missing or invalid credentials |
| No permission | 403 | Valid auth, but not allowed |
| Not found | 404 | Resource doesn't exist |
| Conflict | 409 | Duplicate or state conflict |
| Validation | 422 | Schema valid but business rules fail |
| Server error | 500 | Our fault, log everything |
| 场景 | 状态码 | 适用时机 |
|---|---|---|
| 无效输入 | 400 | 客户端发送了无效数据 |
| 未认证 | 401 | 缺少或无效的凭证 |
| 无权限 | 403 | 认证有效,但无操作权限 |
| 资源不存在 | 404 | 请求的资源不存在 |
| 冲突 | 409 | 重复数据或状态冲突 |
| 验证失败 | 422 | schema合法但违反业务规则 |
| 服务器错误 | 500 | 服务端故障,需记录所有信息 |
5. Async Patterns Principles
5. 异步模式原则
When to Use Each
各模式的适用场景
| Pattern | Use When |
|---|---|
| Sequential async operations |
| Parallel independent operations |
| Parallel where some can fail |
| Timeout or first response wins |
| 模式 | 适用时机 |
|---|---|
| 顺序执行的异步操作 |
| 并行执行的独立操作 |
| 并行执行且允许部分操作失败 |
| 超时或取第一个响应 |
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 offloadI/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
验证库选择
| Library | Best For |
|---|---|
| Zod | TypeScript first, inference |
| Valibot | Smaller bundle (tree-shakeable) |
| ArkType | Performance critical |
| Yup | Existing React Form usage |
| 库 | 最佳适用场景 |
|---|---|
| Zod | TypeScript优先,类型推导 |
| 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
测试策略选择
| Type | Purpose | Tools |
|---|---|---|
| Unit | Business logic | node:test, Vitest |
| Integration | API endpoints | Supertest |
| E2E | Full flows | Playwright |
| 类型 | 目的 | 工具 |
|---|---|---|
| 单元测试 | 测试业务逻辑 | node:test, Vitest |
| 集成测试 | 测试API端点 | Supertest |
| 端到端测试 | 测试完整流程 | Playwright |
What to Test (Priorities)
测试优先级
- Critical paths: Auth, payments, core business
- Edge cases: Empty inputs, boundaries
- Error handling: What happens when things fail?
- Not worth testing: Framework code, trivial getters
- 关键路径:认证、支付、核心业务逻辑
- 边缘场景:空输入、边界值
- 错误处理:故障时的表现
- 无需测试:框架代码、简单的getter方法
Built-in Test Runner (Node.js 22+)
内置测试运行器(Node.js 22+)
node --test src/**/*.test.ts
├── No external dependency
├── Good coverage reporting
└── Watch mode availablenode --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最佳实践的核心是决策制定,而非死记硬背模式。每个项目都应根据自身需求重新考量。",