SKILL.md
readonly只读
name
nodejs-best-practices
description
Node.js 开发原则与决策。框架选择、异步模式、安全性和架构。教授思考方法,而非复制代码。
Node.js 最佳实践
2025 年 Node.js 开发的原则与决策。
学会思考,而非死记硬背代码模式。
何时使用
在进行 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(import/export)
├── 现代标准
├── 更好的 tree-shaking
├── 异步模块加载
└── 用于:新项目
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 | 更小的包(可 tree-shake) |
| ArkType | 性能关键 |
| Yup | 现有 React 表单使用 |
验证理念
- 快速失败:尽早验证
- 具体明确:清晰的错误消息
- 不信任:即使是“内部”数据
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. 应避免的反模式
❌ 不要:
- 对新边缘项目使用 Express(使用 Hono)
- 在生产代码中使用同步方法
- 将业务逻辑放在控制器中
- 跳过输入验证
- 硬编码密钥
- 不经验证信任外部数据
- 用 CPU 工作阻塞事件循环
✅ 应该:
- 根据上下文选择框架
- 当不明确时询问用户偏好
- 对增长中的项目使用分层架构
- 验证所有输入
- 对环境变量使用密钥
- 优化前先分析
10. 决策检查清单
在实现之前:
- [ ] 询问了用户关于技术栈的偏好?
- [ ] 针对此上下文选择了框架?(而非默认)
- [ ] 考虑了部署目标?
- [ ] 规划了错误处理策略?
- [ ] 确定了验证点?
- [ ] 考虑了安全需求?
记住:Node.js 最佳实践是关于决策,而非死记硬背模式。每个项目都应根据其需求进行全新考虑。
限制
- 仅当任务明确符合上述范围时使用此技能。
- 不要将输出视为环境特定验证、测试或专家审查的替代品。
- 如果缺少必要的输入、权限、安全边界或成功标准,请停止并请求澄清。






