SuperAPI AI 工具集

fastify

编程与开发

查看项目

更新于 10-15 12:18

技能介绍

用途

Fastify 是一个高性能的 Node.js Web 框架,围绕 JSON Schema 校验、封装式插件和良好的开发者体验构建。在 TypeScript 中,可将 Fastify 与类型提供器(Zod 或 TypeBox)搭配使用,使运行时校验与静态类型保持一致。本资料聚焦生产环境下的 Fastify(TypeScript)实践模式。

主要能力

  • Schema 校验与类型提供器:Fastify 通过 JSON Schema 校验请求与响应。使用类型提供器可避免重复定义类型。
    • Zod 提供器(推荐用于全栈 TypeScript):由 Zod schema 同时驱动校验与类型。
    • TypeBox 提供器(推荐用于 OpenAPI 与性能场景):使用 TypeBox schema。
  • 插件架构(封装):用插件隔离关注点,便于测试(认证、数据库、路由等)。路由插件可通过 app.register(usersRoutes, { prefix: "/api/v1/users" }) 带前缀注册。
  • 错误处理:通过 setErrorHandler 集中处理意外失败并返回稳定的错误结构,同时记录详细日志。
  • 安全加固(基线):注册标准安全插件并限制载荷,例如 Helmet、CORS、速率限制(@fastify/helmet、@fastify/cors、@fastify/rate-limit)。
  • 优雅关闭:在 SIGINT/SIGTERM 时关闭 HTTP 服务器及下游客户端(数据库、队列)。
  • 测试(Fastify inject):使用 app.inject 在内存中测试路由,无需绑定端口。

使用方式

最小服务器示例(正确写法):

import Fastify from "fastify";

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

app.get("/health", async () => ({ status: "ok" as const }));

await app.listen({ host: "0.0.0.0", port: 3000 });

错误写法:不 await listen 就启动服务器,会与启动过程竞争并隐藏绑定失败:

app.listen({ port: 3000 });
console.log("started"); // races startup and hides bind failures

Zod 提供器示例:

import Fastify from "fastify";
import { z } from "zod";
import { ZodTypeProvider } from "fastify-type-provider-zod";

const app = Fastify({ logger: true }).withTypeProvider<ZodTypeProvider>();

const Query = z.object({ q: z.string().min(1) });

app.get(
  "/search",
  { schema: { querystring: Query } },
  async (req) => {
    return { q: req.query.q };
  },
);

await app.listen({ port: 3000 });

TypeBox 提供器示例:

import Fastify from "fastify";
import { Type } from "@sinclair/typebox";
import { TypeBoxTypeProvider } from "@fastify/type-provider-typebox";

const app = Fastify({ logger: true }).withTypeProvider<TypeBoxTypeProvider>();

const Params = Type.Object({ id: Type.String({ minLength: 1 }) });
const Reply = Type.Object({ id: Type.String() });

app.get(
  "/users/:id",
  { schema: { params: Params, response: { 200: Reply } } },
  async (req) => ({ id: req.params.id }),
);

await app.listen({ port: 3000 });

路由插件示例:

import type { FastifyPluginAsync } from "fastify";

export const usersRoutes: FastifyPluginAsync = async (app) => {
  app.get("/", async () => [{ id: "1" }]);
  app.get("/:id", async (req) => ({ id: (req.params as any).id }));
};

错误处理示例:

app.setErrorHandler((err, req, reply) => {
  req.log.error({ err }, "request failed");
  reply.status(500).send({ error: "internal" as const });
});

安全加固示例:

import helmet from "@fastify/helmet";
import cors from "@fastify/cors";
import rateLimit from "@fastify/rate-limit";

await app.register(helmet);
await app.register(cors, { origin: false });
await app.register(rateLimit, { max: 100, timeWindow: "1 minute" });

优雅关闭示例:

const close = async (signal: string) => {
  app.log.info({ signal }, "shutting down");
  await app.close();
  process.exit(0);
};

process.on("SIGINT", () => void close("SIGINT"));
process.on("SIGTERM", () => void close("SIGTERM"));

测试示例(使用 inject):

import Fastify from "fastify";
import { describe, it, expect } from "vitest";

describe("health", () => {
  it("returns ok", async () => {
    const app = Fastify();
    app.get("/health", async () => ({ status: "ok" as const }));

    const res = await app.inject({ method: "GET", url: "/health" });
    expect(res.statusCode).toBe(200);
    expect(res.json()).toEqual({ status: "ok" });
  });
});

适用场景

  • 需要基于 schema 的校验、可预测的插件机制和高吞吐量的后端服务。
  • 全栈 TypeScript 项目,希望运行时校验与静态类型统一(Zod)。
  • 需要生成 OpenAPI 或对校验性能有要求的场景(TypeBox)。
  • 需要集中错误处理、安全加固、优雅关闭和内存内路由测试的生产服务。

限制与决策参考

  • Fastify vs Express:优先选择 Fastify 用于基于 schema 的校验、可预测的插件和高吞吐量;优先选择 Express 用于最小中间件和最大生态熟悉度。
  • Zod vs TypeBox:优先选择 Zod 用于已统一使用 Zod 的应用代码库(表单、tRPC、共享类型);优先选择 TypeBox 用于 OpenAPI 生成和性能关键的校验。
  • 反模式:跳过请求校验(应在边界用 schema 校验);把所有内容都注册在 main.ts(应将路由和依赖隔离到插件中);返回原始错误对象(应返回稳定错误结构并记录细节)。

资源

  • Fastify: https://www.fastify.io/
  • fastify-type-provider-zod: https://github.com/turkerdev/fastify-type-provider-zod
  • @fastify/type-provider-typebox: https://github.com/fastify/fastify-type-provider-typebox