Fastify 深入浅出的学习

目录

前言

Fastify 中文官方文档

一、Fastify 概览

定义:Fastify 是一个以 性能开发者体验 为目标的 Node.js web 框架

核心特点:

  • 高性能(序列化优化)
  • 插件/封装机制
  • 内置 schema 驱动校验与序列化
  • 优秀的 TypeScript 支持

适用场景:

  • 高并发 API 服务
  • 微服务
  • BFF
  • Serverless

设计原则:

  • 低开销的请求/响应序列化:使用 fast-json-stringify 优化 JSON 序列化。
  • schema-first:用 JSON Schema 描述请求/响应,自动校验和序列化,性能更好。
  • 插件封装(encapsulation):通过插件隔离作用域、依赖注入、避免全局污染。
  • 尽量少做魔法:明确生命周期 hook,便于调试。
  • 插件生态:大量插件(认证、Swagger、Redis、db 客户端等)。

二、入门快速示例(最小可运行)

cpp 复制代码
// index.js
const fastify = require('fastify')({ logger: true });

fastify.get('/', async (request, reply) => {
  return { hello: 'world' };
});

const start = async () => {
  try {
    await fastify.listen({ port: 3000 });
    console.log('Server listening on 3000');
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
};

start();

运行:

bash 复制代码
node index.js

三、核心概念详解

1、请求/响应 API

  • fastify.get/post/put/delete(path, opts, handler):支持返回 Promise 或直接 reply.send()。
  • request:包含 params, query, body, headers, raw(原生 req)。
  • reply:封装了 res,有 reply.code(201).send(obj), reply.header('x', 'y') 等。

示例:

cpp 复制代码
fastify.post('/user/:id', async (request, reply) => {
  const { id } = request.params;
  const body = request.body;
  reply.code(201).send({ id, ...body });
});

2、Schema(JSON Schema)

  • 在 opts.schema 中定义 querystring, params, body, headers, response。
  • Fastify 在接收请求时会校验(基于 AJV),在返回时会序列化/校验响应。
  • schema 带来性能优化(编译一次后复用)。

示例:

cpp 复制代码
fastify.route({
  method: 'POST',
  url: '/user',
  schema: {
    body: { type: 'object', required: ['name'], properties: { name: { type: 'string' } } },
    response: { 201: { type: 'object', properties: { id: { type: 'number' } } } }
  },
  handler: async (req, reply) => { /*...*/ }
});

3、Hooks(生命周期钩子)

常见 hooks(执行顺序):

  • onRequest --- 在请求到达时执行(可用于认证、速率限制)。
  • preParsing --- 原始 body 解析前。
  • preValidation --- 校验前(可修改请求)。
  • preHandler --- 校验后、handler 前(常用于权限)。
  • preSerialization --- 响应序列化前(可处理数据)。
  • onSend --- 响应发送前(最后改 header)。
  • onResponse --- 响应完成后(用于日志、清理)。
  • onTimeout / onError 等特殊钩子。

示例:

cpp 复制代码
fastify.addHook('onRequest', async (req, reply) => {
  // 认证
});

4、Decorators(扩展实例/请求/回复)

  • fastify.decorate(name, value):添加到 Fastify 实例(可在插件中注入)。
  • fastify.decorateRequest('foo', null) / fastify.decorateReply('bar', null):为 request/reply 增加方法/字段。

【注意】:装饰要与 encapsulation 协调,避免跨插件冲突

示例:

cpp 复制代码
fastify.decorate('utils', { uuid: () => '...' });
fastify.decorateRequest('user', null);

5、Encapsulation(封装)

  • 插件之间默认是封装的:子插件的 decorate 不会泄露给父插件之外的插件,除非你显式设置 fastify.register() 的 options。
  • 这是 Fastify 防止全局污染的核心机制:你可以为每个模块创建独立上下文(依赖注入、安全隔离)。

四、插件系统(最重要的机制)

1、注册插件

cpp 复制代码
fastify.register(require('@fastify/sqlite'), { dbFile: ':memory:' });

注册后插件可以使用 fastify.decorate 注入 db 实例或其他工具。

2、fastify-plugin

用于将一个函数标记为 Fastify 插件(并可设置依赖)。

示例:

cpp 复制代码
const fp = require('fastify-plugin');

module.exports = fp(async (fastify, opts) => {
  fastify.decorate('service', new Service(opts));
}, { name: 'my-service', dependencies: ['@fastify/sqlite'] });

3、autoload(自动加载)

fastify-autoload 可以按照目录结构自动加载插件、路由,适合大型项目约定成俗的结构。

五、性能相关(为什么 Fastify 快?如何优化)

1、性能来源

  • JSON 序列化使用 fast-json-stringify,针对 schema 生成快速序列化函数(比 JSON.stringify 在某些场景快)。
  • Schema 驱动减少运行期校验开销(编译一次复用)。
  • 更少中间件抽象层,低 overhead。

2、性能优化建议

  • 使用 schema:为请求和响应都加 schema,能显著提速。
  • 避免在热路由上做大量同步工作:同步计算会阻塞事件循环。
  • 使用 reply.serializer(自定义序列化)仅在必要时。
  • 复用单例资源(DB 连接池),但注意封装作用域。
  • 开启 HTTP keep-alive、合理设置 maxSockets(在客户端或代理中)。
  • 批量响应/压缩:使用 @fastify/compress 启用 gzip/brotli。
  • 在高并发下使用 cluster 或进程管理(PM2 / Kubernetes)扩展

3、Threadpool 与 CPU 密集

  • Fastify 本身是单线程 JS。CPU 耗时操作应用 worker_threads 或把任务下放给后台微服务。

六、TypeScript 与 Fastify(企业级要点)

Fastify 对 TypeScript 支持很好,但要注意类型扩展与插件的类型。

1、基本 TypeScript 快速示例

cpp 复制代码
import Fastify from 'fastify';
const fastify = Fastify({ logger: true });

fastify.get('/', async (request, reply) => {
  return { hello: 'world' };
});

2、类型扩展(decorate、plugin)

如果插件 decorate 了 fastify.foo,需在类型声明中扩展 FastifyInstance 接口:

cpp 复制代码
declare module 'fastify' {
  interface FastifyInstance {
    service: MyService;
  }
}

对于 request.body 等,使用泛型指定 route 的 schema types:

cpp 复制代码
fastify.post<{ Body: { name: string } }>('/user', async (request, reply) => {
  const name = request.body.name;
});

3、推荐工具链

  • ts-node-dev(开发阶段)
  • 构建到 JS(tsc / esbuild)用于生产
  • 使用 fastify-cli 创建 TS 项目模板

七、路由与分层架构(最佳实践)

1、项目结构(建议)

cpp 复制代码
src/
  server.ts
  plugins/
    db.ts
    auth.ts
  routes/
    users/
      index.ts        // 注册子路由
      handlers.ts
      schema.ts
  services/
    userService.ts
  utils/
  types/

使用 fastify-autoload 自动加载 plugins 和 routes,配合 fastify-plugin 做依赖注入。

2、路由封装示例

routes/users/index.ts:

cpp 复制代码
import { FastifyPluginAsync } from 'fastify';
import handlers from './handlers';
import schema from './schema';

const users: FastifyPluginAsync = async (fastify) => {
  fastify.get('/', { schema: schema.list }, handlers.list);
  fastify.get('/:id', { schema: schema.get }, handlers.get);
};

export default users;

八、安全(必做项)

  • 使用 @fastify/helmet 添加安全头(CSP、X-Frame、XSS)。
  • 使用 @fastify/rate-limit 防止滥用(DoS、暴力破解)。
  • 使用 @fastify/cors 配置 CORS 白名单。
  • 校验输入(schema)以防注入攻击。
  • 在生产中禁用详细错误输出,使用统一错误结构(error handler)。

示例:

cpp 复制代码
fastify.register(require('@fastify/helmet'));
fastify.register(require('@fastify/rate-limit'), { max: 100, timeWindow: '1 minute' });
fastify.register(require('@fastify/cors'), { origin: ['https://your.app'] });

九、错误处理与日志

  • 使用 setErrorHandler 自定义错误格式与日志:
cpp 复制代码
fastify.setErrorHandler((error, request, reply) => {
  request.log.error(error);
  reply.status(error.statusCode || 500).send({ error: error.message });
});
  • 内置 logger 使用 pino,性能优秀。生产建议把日志输出到 stdout 并由日志聚合系统采集。

十、测试(单元、集成、端到端)

推荐使用 tap(Fastify 官方示例)或 Jest + supertest。fastify.inject() 可以直接模拟请求,不需要启动网络端口,非常适合单元/集成测试。

示例(tap):

cpp 复制代码
const t = require('tap');
const Fastify = require('fastify');

t.test('GET /', async t => {
  const app = Fastify();
  app.get('/', async () => ({ ok: true }));
  const res = await app.inject({ method: 'GET', url: '/' });
  t.equal(res.statusCode, 200);
  t.same(JSON.parse(res.payload), { ok: true });
  await app.close();
});

十一、常用与推荐插件(企业级清单)

  • @fastify/helmet --- 安全头
  • @fastify/cors --- CORS
  • @fastify/compress --- gzip/brotli
  • @fastify/jwt --- JWT 认证
  • @fastify/oauth2 --- OAuth2 客户端
  • @fastify/rate-limit --- 限流
  • @fastify/swagger / @fastify/openapi-glue --- 自动生成 API 文档
  • @fastify/sqlite, @fastify/postgres --- DB 连接
  • fastify-autoload --- 自动加载插件/路由
  • fastify-plugin --- 标记插件
  • fastify-sensible --- 常用响应工具(httpErrors 等)
  • fastify-formbody --- 解析 form body
  • fastify-static --- 静态文件
  • fastify-multipart --- 文件上传(stream 支持)

十二、Express 到 Fastify 的迁移实务(要点)

  • 路由语法类似,但要改造 middleware:Express 中间件需用 fastify-express 或改写为 Fastify hooks/plugins。
  • 注意:Express 全局中间件在 Fastify 中要转成 hook 或封装插件。
  • req/res 对象不同,Fastify 提供 request.raw 和 reply.raw 访问原生 node 对象。
  • 推荐逐步迁移:把路由逐个转换为 Fastify plugin,使用 fastify.register() 提高可维护性。

十三、部署与扩容(生产实践)

  • 多进程:使用 cluster、PM2 或容器编排(K8s)将应用扩展到多核。
  • 反向代理:通常前面放 Nginx / Envoy 做 TLS 终端、静态缓存、负载均衡。
  • 资源监控:pino 日志 + Prometheus 指标导出(prom-client)+ Grafana。
  • 健康探针:提供 /healthz readiness 和 liveness endpoints。
  • 容器镜像:使用多阶段构建,生产镜像只包含运行时产物;用非 root 用户运行。

示例 Dockerfile:

cpp 复制代码
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app ./
ENV NODE_ENV=production
RUN npm ci --production
USER node
CMD ["node", "dist/index.js"]

十四、监控指标 & 健康检查(建议)

导出常见指标(响应时间、错误率、event loop lag、heap usage),并在 K8s HPA 中使用自定义指标。

快速导出 event loop lag:

cpp 复制代码
const { monitorEventLoopDelay } = require('perf_hooks');
const h = monitorEventLoopDelay();
h.enable();
setInterval(() => {
  // expose h.mean/h.max to prometheus
}, 5000);

十五、常见问题与排查指南

  • 高延迟但 CPU 很低:可能外部依赖慢(DB、HTTP),加超时与熔断,检查连接池。
  • 内存泄漏 / OOM:进行 heapdump 分析,检查全局缓存、未关闭的连接与未清的定时器。
  • 大量 502/504:代理超时、后端资源耗尽或死锁。检查 Nginx 配置与 keepalive。
  • schema 校验报错:查看 schema 的 compiled errors,注意 additionalProperties 默认行为。
  • 插件冲突:检查 encapsulation,是否意外 decorate 全局变量名重复。

十六、性能调优 checklist(落地)

  • 使用 schema(请求 + 响应)。
  • 在高频 API 使用 response.schema + fast-json-stringify 的序列化优势。
  • 避免常见阻塞(同步 IO、大对象 JSON.stringify)。
  • 启用 @fastify/compress 并选择合适压缩算法(brotli 在静态资源上优异)。
  • 使用 pino 并输出到 stdout(不会阻塞主线程)。
  • 对大文件上传使用 stream(fastify-multipart)。
  • 将 CPU 密集任务用 worker_threads 容器化或微服务化。
  • 在 K8s 中设置合理的资源 requests/limits,使用 HPA。

十七、实战示例:一个带 Auth、DB、Swagger 的项目骨架(关键代码片段)

src/server.ts:

cpp 复制代码
import Fastify from 'fastify';
import autoload from 'fastify-autoload';
import path from 'path';

const fastify = Fastify({ logger: true });

fastify.register(require('@fastify/helmet'));
fastify.register(require('@fastify/cors'), { origin: true });
fastify.register(require('@fastify/compress'));
fastify.register(autoload, { dir: path.join(__dirname, 'plugins') });
fastify.register(autoload, { dir: path.join(__dirname, 'routes') });

fastify.listen({ port: 3000 })
  .then(() => fastify.log.info('Server started'));

src/plugins/db.ts:

cpp 复制代码
import fp from 'fastify-plugin';
import { Database } from 'some-db-lib';

export default fp(async function (fastify, opts) {
  const db = new Database(opts);
  fastify.decorate('db', db);
  fastify.addHook('onClose', async () => { await db.close(); });
});

src/routes/users/index.ts:

cpp 复制代码
import fp from 'fastify-plugin';
export default async function (fastify) {
  fastify.get('/users', {
    schema: { response: { 200: { type: 'array', items: { type: 'object' } } } }
  }, async (req, reply) => {
    return await fastify.db.getUsers();
  });
}

十八、Fastify 与其他框架对比(简要)

  • Express:生态大、学习门槛低,但性能与 schema 支持较弱。迁移到 Fastify 可获得性能和类型优势。
  • Koa:语法优雅、基于 async/await,中间件更简洁;Fastify 更注重性能与插件封装。
  • NestJS:企业级架构(DI、模块化)。Nest 可以选择 Fastify 作为底层 HTTP adapter,得到两者优点。
相关推荐
黑金IT1 年前
Fastify Swagger:自动化API文档生成与展示
nodejs·swagger·fastify