Fastify

作者 bobmatnyc718070a7d622MIT77 個星標收錄於 2026年10月8日更新於 2026年10月8日儲存庫2 個月前更新

Production Fastify (TypeScript) patterns: schema validation, plugins, typed routes, error handling, security hardening, logging, testing with inject, and graceful shutdown

AI 產生的概覽

用於建構正式環境 Fastify TypeScript API 的參考模式,涵蓋結構描述驗證、外掛與測試。

功能
提供在 TypeScript 中建構 Fastify 後端的指引與程式碼範例,涵蓋使用 Zod 或 TypeBox 型別提供者的結構描述驗證、外掛封裝、具型別的路由、集中式錯誤處理、安全中介軟體、記錄、優雅關閉,以及使用 inject 進行記憶體內路由測試。它是說明性參考資料而非可執行的工具,產出的是模式與程式碼片段,而不是檔案或執行中的服務。
適用情境
適用於建構或審查以 Fastify 為基礎的 TypeScript Node 後端,並希望取得驗證、外掛結構、錯誤處理、安全強化或測試方面成熟模式的時候。也可用於在 Fastify 與 Express 之間,或在 Zod 與 TypeBox 之間做選擇時。
執行需求
不需要指令碼或工具,僅為說明性內容。若要實作範例,需要 Node.js 執行環境以及 Fastify,選用套件包括 Zod、TypeBox、@fastify/helmet、@fastify/cors、@fastify/rate-limit,以及 Vitest 等測試執行器。

Fastify (TypeScript) - Production Backend Framework

Overview

Fastify is a high-performance Node.js web framework built around JSON schema validation, encapsulated plugins, and great developer ergonomics. In TypeScript, pair Fastify with a type provider (Zod or TypeBox) to keep runtime validation and static types aligned.

Quick Start

Minimal server

✅ Correct: basic server with typed response

ts
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 });

❌ Wrong: start server without awaiting listen

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

Schema Validation + Type Providers

Fastify validates requests/responses via JSON schema. Use a type provider to avoid duplicating types.

Zod provider (recommended for full-stack TypeScript)

✅ Correct: Zod schema drives validation + types

ts
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 provider (recommended for OpenAPI + performance)

✅ Correct: TypeBox schema

ts
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 });

Plugin Architecture (Encapsulation)

Use plugins to keep concerns isolated and testable (auth, db, routes).

✅ Correct: route plugin

ts
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 }));};

✅ Correct: register with a prefix

ts
app.register(usersRoutes, { prefix: "/api/v1/users" });

Error Handling

Centralize unexpected failures and return stable error shapes.

✅ Correct: setErrorHandler

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

Security Hardening (Baseline)

Add standard security plugins and enforce payload limits.

✅ Correct: Helmet + CORS + rate limiting

ts
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" });

Graceful Shutdown

Close HTTP server and downstream clients (DB, queues) on SIGINT/SIGTERM.

✅ Correct: close on signals

ts
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"));

Testing (Fastify inject)

Test routes in-memory without binding ports.

✅ Correct: inject request

ts
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" });  });});

Decision Trees

Fastify vs Express

  • Prefer Fastify for schema-based validation, predictable plugins, and high throughput.
  • Prefer Express for minimal middleware and maximal ecosystem familiarity.

Zod vs TypeBox

  • Prefer Zod for app codebases that already standardize on Zod (forms, tRPC, shared types).
  • Prefer TypeBox for OpenAPI generation and performance-critical validation.

Anti-Patterns

  • Skip request validation; validate at boundaries with schemas.
  • Register everything in main.ts; isolate routes and dependencies into plugins.
  • Return raw error objects; return stable error shapes and log the details.

Resources

來源與署名

來源:bobmatnyc/claude-mpm-skills位於toolchains/typescript/frameworks/fastify提交718070a

授權條款: MIT

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架