名称: nodejs最佳实践
描述: 2025年Node.js开发的原则和决策。框架选择、异步模式、安全和架构。教思考,而非复制代码。
允许工具: 读取、写入、编辑、全局搜索、文本搜索
Node.js 最佳实践
2025年Node.js开发的原则和决策。
学习思考,而非记忆代码模式。
⚠️ 如何使用此技能
此技能教授决策原则,而非固定代码供复制。
- 当不明确时,询问用户偏好
- 基于上下文选择框架/模式
- 不要每次都默认使用相同解决方案
1. 框架选择 (2025)
决策树
您正在构建什么?
│
├── 边缘/无服务器(Cloudflare、Vercel)
│ └── Hono(零依赖、超快冷启动)
│
├── 高性能API
│ └── Fastify(比Express快2-3倍)
│
├── 企业/团队熟悉度
│ └── NestJS(结构化、依赖注入、装饰器)
│
├── 遗留/稳定/最大生态系统
│ └── Express(成熟、最多中间件)
│
└── 全栈与前端集成
└── Next.js API路由或tRPC
比较原则
| 因素 |
Hono |
Fastify |
Express |
| 最适合 |
边缘、无服务器 |
性能 |
遗留、学习 |
| 冷启动 |
最快 |
快 |
中等 |
| 生态系统 |
增长中 |
好 |
最大 |
| TypeScript |
原生 |
优秀 |
良好 |
| 学习曲线 |
低 |
中等 |
低 |
选择时要问的问题:
- 部署目标是什么?
- 冷启动时间是否关键?
- 团队是否有现有经验?
- 是否有遗留代码需要维护?
2. 运行时考虑 (2025)
原生TypeScript
Node.js 22+: --experimental-strip-types
├── 直接运行 .ts 文件
├── 简单项目无需构建步骤
└── 考虑用于:脚本、简单API
模块系统决策
ESM(导入/导出)
├── 现代标准
├── 更好的树摇优化
├── 异步模块加载
└── 用于:新项目
CommonJS(require)
├── 遗留兼容性
├── 更多npm包支持
└── 用于:现有代码库、某些边缘情况
运行时选择
| 运行时 |
最适合 |
| Node.js |
通用目的、最大生态系统 |
| Bun |
性能、内置打包器 |
| Deno |
安全第一、内置TypeScript |
3. 架构原则
分层结构概念
请求流程:
│
├── 控制器/路由层
│ ├── 处理HTTP细节
│ ├── 边界输入验证
│ └── 调用服务层
│
├── 服务层
│ ├── 业务逻辑
│ ├── 框架无关
│ └── 调用仓库层
│
└── 仓库层
├── 仅数据访问
├── 数据库查询
└── ORM交互
为什么这很重要:
- 可测试性:独立模拟各层
- 灵活性:更换数据库而不影响业务逻辑
- 清晰性:每层单一职责
何时简化:
- 小型脚本 → 单文件可行
- 原型 → 结构较少可接受
- 总是问:“这会增长吗?”
4. 错误处理原则
集中错误处理
模式:
├── 创建自定义错误类
├── 从任何层抛出
├── 在顶层捕获(中间件)
└── 格式化一致响应
错误响应哲学
客户端获得:
├── 适当的HTTP状态码
├── 程序化处理的错误代码
├── 用户友好消息
└── 无内部细节(安全!)
日志获得:
├── 完整堆栈跟踪
├── 请求上下文
├── 用户ID(如果适用)
└── 时间戳
状态码选择
| 情况 |
状态码 |
何时使用 |
| 错误输入 |
400 |
客户端发送无效数据 |
| 无认证 |
401 |
缺少或无效凭据 |
| 无权限 |
403 |
有效认证但不允许 |
| 未找到 |
404 |
资源不存在 |
| 冲突 |
409 |
重复或状态冲突 |
| 验证失败 |
422 |
架构有效但业务规则失败 |
| 服务器错误 |
500 |
我们的错误,记录一切 |
5. 异步模式原则
何时使用每种
| 模式 |
使用时机 |
async/await |
顺序异步操作 |
Promise.all |
并行独立操作 |
Promise.allSettled |
并行操作其中一些可能失败 |
Promise.race |
超时或第一个响应获胜 |
事件循环意识
I/O密集型(异步有帮助):
├── 数据库查询
├── HTTP请求
├── 文件系统
└── 网络操作
CPU密集型(异步无帮助):
├── 加密操作
├── 图像处理
├── 复杂计算
└── → 使用工作线程或卸载
避免事件循环阻塞
- 生产中绝不同步方法(如fs.readFileSync等)
- 卸载CPU密集型工作
- 使用流处理大型数据
6. 验证原则
在边界验证
验证位置:
├── API入口点(请求体/参数)
├── 数据库操作前
├── 外部数据(API响应、文件上传)
└── 环境变量(启动时)
验证库选择
| 库 |
最适合 |
| Zod |
TypeScript优先、推断 |
| Valibot |
更小包(可树摇优化) |
| ArkType |
性能关键 |
| Yup |
现有React表单使用 |
验证哲学
- 快速失败:尽早验证
- 具体:清晰的错误消息
- 不信任:即使是“内部”数据
7. 安全原则
安全检查表(非代码)
- [ ] 输入验证:所有输入已验证
- [ ] 参数化查询:无字符串拼接SQL
- [ ] 密码哈希:bcrypt或argon2
- [ ] JWT验证:始终验证签名和过期
- [ ] 速率限制:防止滥用
- [ ] 安全头:Helmet.js或等效
- [ ] HTTPS:生产环境全程使用
- [ ] CORS:正确配置
- [ ] 秘密:仅环境变量
- [ ] 依赖:定期审计
安全心态
不信任任何事物:
├── 查询参数 → 验证
├── 请求体 → 验证
├── 头信息 → 验证
├── Cookie → 验证
├── 文件上传 → 扫描
└── 外部API → 验证响应
8. 测试原则
测试策略选择
| 类型 |
目的 |
工具 |
| 单元测试 |
业务逻辑 |
node:test、Vitest |
| 集成测试 |
API端点 |
Supertest |
| 端到端测试 |
完整流程 |
Playwright |
测试内容(优先级)
- 关键路径:认证、支付、核心业务
- 边缘情况:空输入、边界
- 错误处理:失败时会发生什么?
- 不值得测试:框架代码、简单获取器
内置测试运行器(Node.js 22+)
node --test src/**/*.test.ts
├── 无外部依赖
├── 良好的覆盖率报告
└── 支持监视模式
10. 避免的反模式
❌ 不要做:
- 新边缘项目使用Express(用Hono)
- 生产代码中使用同步方法
- 将业务逻辑放在控制器中
- 跳过输入验证
- 硬编码秘密
- 不验证就信任外部数据
- 用CPU工作阻塞事件循环
✅ 做:
- 基于上下文选择框架
- 当不明确时询问用户偏好
- 增长项目使用分层架构
- 验证所有输入
- 秘密使用环境变量
- 优化前先分析
11. 决策检查表
实施前:
- [ ] 询问用户堆栈偏好?
- [ ] 为此上下文选择框架?(不只是默认)
- [ ] 考虑部署目标?
- [ ] 计划错误处理策略?
- [ ] 识别验证点?
- [ ] 考虑安全要求?
记住:Node.js最佳实践是关于决策制定,而非记忆模式。每个项目都应根据其需求进行新鲜考虑。