nodejs-backend
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseNode.js Backend
Node.js后端开发
Framework Selection
框架选择
| Context | Choose | Why |
|---|---|---|
| Edge/Serverless | Hono | Zero-dep, fastest cold starts |
| Performance API | Fastify | 2-3x faster than Express, built-in schema validation |
| Enterprise/team | NestJS | DI, decorators, structured conventions |
| Legacy/ecosystem | Express | Most middleware, widest adoption |
Ask user: deployment target, cold start needs, team experience, existing codebase.
| 场景 | 选择框架 | 原因 |
|---|---|---|
| 边缘计算/无服务器 | Hono | 零依赖,冷启动速度最快 |
| 高性能API | Fastify | 速度比Express快2-3倍,内置Schema验证 |
| 企业级/团队项目 | NestJS | 依赖注入、装饰器、结构化规范 |
| 遗留项目/生态兼容 | Express | 中间件最丰富,普及率最高 |
询问用户:部署目标、冷启动需求、团队技术经验、现有代码库情况。
Architecture
架构设计
src/
├── routes/ # HTTP: parse request, call service, format response
├── middleware/ # Auth, validation, rate limiting, logging
├── services/ # Business logic (no HTTP types)
├── repositories/ # Data access only (queries, ORM)
├── config/ # Env, DB pool, constants
└── types/ # Shared TypeScript interfaces- Routes never contain business logic
- Services never import Request/Response
- Repositories never throw HTTP errors
- For scripts/prototypes: single file is fine — ask "will this grow?"
src/
├── routes/ # HTTP层:解析请求、调用服务、格式化响应
├── middleware/ # 鉴权、验证、请求频率限制、日志
├── services/ # 业务逻辑(无HTTP类型依赖)
├── repositories/ # 仅数据访问(查询、ORM)
├── config/ # 环境变量、数据库连接池、常量
└── types/ # 共享TypeScript接口- 路由层绝不包含业务逻辑
- 服务层绝不导入Request/Response类型
- 数据访问层绝不抛出HTTP错误
- 对于脚本/原型项目:单文件实现即可——询问“该项目是否会扩展?”
TypeScript Rules
TypeScript规则
- Use for type-only imports — eliminates runtime overhead
import type { } - Prefer for object shapes (2-5x faster type resolution than intersections)
interface - Prefer over
unknown— forces explicit narrowingany - Use as single source of truth — never duplicate types and schemas
z.infer<typeof Schema> - Minimize assertions — use type guards instead
as - Add explicit return types to exported functions (faster declaration emit)
- Untyped package? in
declare module 'pkg' { const v: unknown; export default v; }types/ambient.d.ts
- 使用进行仅类型导入——消除运行时开销
import type { } - 优先用定义对象结构(类型解析速度比交叉类型快2-5倍)
interface - 优先用而非
unknown——强制显式类型收窄any - 使用作为唯一数据源——绝不重复定义类型和Schema
z.infer<typeof Schema> - 尽量减少类型断言——改用类型守卫
as - 为导出函数添加显式返回类型(加快声明文件生成速度)
- 遇到无类型的包?在中添加
types/ambient.d.tsdeclare module 'pkg' { const v: unknown; export default v; }
Validation
验证方案
Zod (TypeScript inference) or TypeBox (Fastify native). Validate at boundaries only: request entry, before DB ops, env vars at startup. Use , , , , for DRY schemas.
.extend().pick().omit().partial().merge()Zod(TypeScript类型推导)或TypeBox(Fastify原生支持)。仅在边界处进行验证:请求入口、数据库操作前、启动时验证环境变量。使用、、、、实现DRY(不重复)的Schema。
.extend().pick().omit().partial().merge()Error Handling
错误处理
Custom error hierarchy: → , , , ,
AppError(message, statusCode, isOperational)ValidationError(400)NotFoundError(404)UnauthorizedError(401)ForbiddenError(403)ConflictError(409)Centralized handler middleware:
- → return
AppErrorwith statusCode{ error: message } - Unknown → log full stack, return 500 + generic message in production
- Async wrapper:
const asyncHandler = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);
Codes: 400 bad input | 401 no auth | 403 no permission | 404 missing | 409 conflict | 422 business rule | 429 rate limited | 500 server fault
自定义错误层级: → 、、、、
AppError(message, statusCode, isOperational)ValidationError(400)NotFoundError(404)UnauthorizedError(401)ForbiddenError(403)ConflictError(409)集中式错误处理中间件:
- → 返回
AppError及对应状态码{ error: message } - 未知错误 → 在生产环境中记录完整堆栈信息,返回500状态码和通用错误信息
- 异步包装器:
const asyncHandler = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);
状态码说明:400(输入错误)| 401(未授权)| 403(无权限)| 404(资源不存在)| 409(资源冲突)| 422(业务规则不满足)| 429(请求频率超限)| 500(服务器故障)
API Design
API设计规范
- Resources: plural nouns (), max 2 nesting levels (
/users)/users/:id/orders - Methods: GET read | POST create | PUT replace | PATCH partial | DELETE remove
- Versioning: URL path
/api/v1/ - Response:
{ data, pagination?: { page, limit, total, totalPages } } - Errors:
{ error: { code, message, details? } } - Queries:
?page=1&limit=20&status=active&sort=createdAt,desc - Return header on 201. Use 204 for successful DELETE with no body.
Location
- 资源命名:使用复数名词(),最多嵌套2层(
/users)/users/:id/orders - 请求方法:GET(读取)| POST(创建)| PUT(替换)| PATCH(部分更新)| DELETE(删除)
- 版本控制:URL路径
/api/v1/ - 响应格式:
{ data, pagination?: { page, limit, total, totalPages } } - 错误响应:
{ error: { code, message, details? } } - 查询参数:
?page=1&limit=20&status=active&sort=createdAt,desc - 201状态码返回时需附带响应头。成功删除且无返回体时使用204状态码。
Location
Async Patterns
异步模式
| Pattern | Use When |
|---|---|
| Sequential operations |
| Parallel independent ops |
| Parallel, some may fail |
| Timeout or first-wins |
Never / sync methods in production. Offload CPU work to worker threads. Stream large payloads.
readFileSync| 模式 | 使用场景 |
|---|---|
| 串行操作 |
| 并行独立操作 |
| 并行操作,允许部分失败 |
| 超时控制或先完成优先 |
生产环境中绝不要使用/同步方法。将CPU密集型任务卸载到工作线程。流式处理大负载数据。
readFileSyncDiscipline
开发准则
- For non-trivial changes, pause and ask: "is there a more elegant way?" Skip for obvious fixes.
- Simplicity first — every change as simple as possible, impact minimal code
- Only touch what's necessary — avoid introducing unrelated changes
- No hacky workarounds — if a fix feels wrong, step back and implement the clean solution
- 对于非琐碎的代码变更,先暂停思考:“有没有更优雅的实现方式?”明显的修复可跳过此步骤。
- 简洁优先——每个变更尽可能简单,影响代码范围最小
- 只修改必要的代码——避免引入无关变更
- 拒绝投机取巧的解决方案——如果某个修复感觉不对劲,退一步实现更简洁的方案
References
参考资料
- TypeScript config — tsconfig, ESM, branded types, compiler performance
- Security — JWT, password hashing, rate limiting, OWASP
- Database & production — connection pooling, transactions, Docker, logging
- TypeScript配置 — tsconfig、ESM、品牌类型、编译器性能
- 安全防护 — JWT、密码哈希、请求频率限制、OWASP
- 数据库与生产环境 — 连接池、事务、Docker、日志记录