从 Express 迁移到 Fastify:完整实战指南

Express 迁移 Fastify 的系统方法:架构差异、请求响应处理转换、中间件到插件的改造、Schema 校验迁移与性能优化。本文附迁移清单。

最佳实践
应用接口与数据存储连接插画

迁移的核心是四个转变——中间件换成插件/钩子、手写校验换成 JSON Schema、回调错误处理换成 async 直抛、res.send 语义对齐。Fastify 官方提供 @fastify/express 兼容层,支持"先跑起来再逐步改写"的渐进迁移。

架构差异速览

维度 Express Fastify
扩展机制 中间件(线性) 插件 + 钩子(封装作用域)
校验 外部库(joi/zod) 内建 JSON Schema
错误处理 4 参错误中间件 setErrorHandler + async 直抛
序列化 JSON.stringify 编译型 fast-json-stringify
Node 要求 16+ 20+

请求与响应的转换

// Express
app.get('/users/:id', (req, res) => {
  res.status(200).json({ id: req.params.id });
});

// Fastify
fastify.get('/users/:id', async (request, reply) => {
  return { id: request.params.id };  // return 即响应,或 reply.send()
});

映射关系:req.query→request.query、req.params→request.params、req.body→request.body、res.status(x).json(y)→reply.code(x).send(y)。async handler 的未捕获异常自动进入错误处理器——Express 4 的 try/catch 脚手架可以全删。

中间件到插件的改造

Express 中间件(req, res, next)在 Fastify 里没有直接对应物——改造成钩子:

// Express 的认证中间件
function auth(req, res, next) {
  const token = req.headers.authorization;
  if (!token) return res.status(401).send('未认证');
  req.user = verify(token);
  next();
}

// Fastify 改造成钩子
fastify.decorate('authenticate', async (request, reply) => {
  const token = request.headers.authorization;
  if (!token) return reply.code(401).send({ error: '未认证' });
  request.user = verify(token);
});

// 路由上使用
fastify.get('/protected', { preHandler: [fastify.authenticate] }, handler);

过渡方案:@fastify/express 插件可直接挂载 Express 中间件——迁移初期用它跑通,再逐个改写为钩子。

校验迁移:从库到 Schema

// Express + Joi
const schema = Joi.object({ email: Joi.string().email().required() });
app.post('/users', (req, res) => {
  const { error } = schema.validate(req.body);
  if (error) return res.status(400).send(error.details);
});

// Fastify:Schema 内建,校验失败自动 400
fastify.post('/users', {
  schema: {
    body: {
      type: 'object',
      required: ['email'],
      properties: { email: { type: 'string', format: 'email' } },
    },
  },
}, handler);

额外收益:response Schema 启用编译型序列化——响应吞吐显著提升,这是迁移的性能红利。

测试策略调整

supertest 换成 Fastify 的 inject(无需起真实端口):

const res = await fastify.inject({ method: 'GET', url: '/users/1' });
expect(res.statusCode).toBe(200);

inject 测试更快且省去端口管理——测试套件通常因此提速。

迁移清单

  1. 装 @fastify/express 兼容层,Express app 挂进 Fastify 跑通;
  2. 逐路由迁移:handler 改 async、res.send 改 return/reply.send;
  3. 中间件逐个改写为钩子/装饰器,迁完删兼容层;
  4. 校验逻辑迁移到 JSON Schema;
  5. 补 response Schema 拿序列化性能;
  6. 测试从 supertest 切到 inject;
  7. 压测对比,收编性能收益。

验证接口上线后的延迟与错误

从 Express 迁移到 Fastify完成开发验证后,可选一条包含数据库或外部调用的业务路径接入观测云。按 Node.js APM 文档确认探针初始化顺序与框架兼容性,检查请求及下游 Span 是否连续,再按文档注入日志关联字段。以路由模板或稳定的操作名聚合,避免用用户 ID、动态 URL 或完整 GraphQL 查询作为高基数标签。

接入文档:Node.js APM 接入 · 日志采集 · 日志与链路关联。

常见问题(FAQ)

Q:必须全量重写吗?

不必。@fastify/express 兼容层 + 逐路由迁移,风险可控、随时可回退。新功能直接在 Fastify 侧开发。

Q:迁移后性能提升多少?

收益取决于路由、序列化与框架开销占比;迁移有适配成本,应以代表性请求比较吞吐、P95/P99 延迟和错误率。

Q:Express 5 都出了,还有必要迁吗?

Express 5 补的是"旧账"(async 错误处理等);Fastify 的 Schema 体系、插件封装、序列化编译是架构代差。性能与工程化敏感的项目,迁移仍有明确价值。


参考文档

本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。

延伸阅读

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台