Express & Fastify
规则
- 按域组织路由:
routes/users.ts、routes/orders.ts — 每个资源一个路由器
- 使用 Zod 验证所有输入:在处理之前解析
req.body、req.params、req.query
- 集中式错误处理器:Express 中使用 catch-all 中间件,Fastify 中使用
setErrorHandler
- 返回一致的错误格式:
{ error: { code: string, message: string, details?: unknown } }
- TypeScript:使用泛型类型化请求/响应 —
Request<Params, ResBody, ReqBody, Query>
- 中间件顺序:CORS、body 解析器、认证、速率限制、路由、错误处理器
- 使用异步路由处理器 — 用错误捕获包装或使用
express-async-errors
- Fastify:使用插件系统封装路由和装饰器 —
fastify.register()
- Fastify schemas:定义 JSON Schema 或使用
@fastify/type-provider-zod 进行验证 + 序列化
- 健康检查:
GET /health — 返回 200 并包含数据库/Redis 连接状态
- 优雅关闭:监听 SIGTERM 信号,停止接受新连接,完成正在处理的请求
模式
import { Router } from "express";
import { z } from "zod";
const router = Router();
const CreateUserSchema = z.object({ email: z.string().email(), name: z.string().min(1) });
router.post("/users", async (req, res, next) => {
try {
const body = CreateUserSchema.parse(req.body);
const user = await createUser(body);
res.status(201).json(user);
} catch (err) { next(err); }
});
import Fastify from "fastify";
import { serializerCompiler, validatorCompiler, ZodTypeProvider } from "fastify-type-provider-zod";
const app = Fastify().withTypeProvider<ZodTypeProvider>();
app.setValidatorCompiler(validatorCompiler);
app.setSerializerCompiler(serializerCompiler);
app.post("/users", { schema: { body: CreateUserSchema } }, async (req) => {
return createUser(req.body);
});
避免
- 将业务逻辑放在路由处理程序中 — 提取到服务/领域层
- 忘记 Express 中的异步错误处理 — 未捕获的拒绝会导致进程崩溃
- 在一个文件中导入所有路由 — 使用
router.use() 或 Fastify 插件来实现模块化
- 跳过输入验证,因为“前端已经验证了” — 始终在服务器端进行验证