fastify

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Fastify Core Knowledge

Fastify 核心知识

Full Reference: See advanced.md for WebSocket authentication, room management, heartbeat patterns, message validation, and Redis scaling.
Deep Knowledge: Use
mcp__documentation__fetch_docs
with technology:
fastify
for comprehensive documentation.
完整参考:查看 advanced.md 了解 WebSocket 认证、房间管理、心跳模式、消息验证和 Redis 扩容相关内容。
深度知识:调用
mcp__documentation__fetch_docs
并指定 technology 为
fastify
即可获取完整文档。

Basic Setup

基础配置

ts
import Fastify from 'fastify';
import cors from '@fastify/cors';

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

await app.register(cors);
await app.register(userRoutes, { prefix: '/api/users' });

app.listen({ port: 3000, host: '0.0.0.0' });
ts
import Fastify from 'fastify';
import cors from '@fastify/cors';

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

await app.register(cors);
await app.register(userRoutes, { prefix: '/api/users' });

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

Route with Schema Validation

带 Schema 验证的路由

ts
import { FastifyPluginAsync } from 'fastify';
import { Type, Static } from '@sinclair/typebox';

const UserSchema = Type.Object({
  name: Type.String(),
  email: Type.String({ format: 'email' }),
});

type User = Static<typeof UserSchema>;

const routes: FastifyPluginAsync = async (app) => {
  app.post<{ Body: User }>('/', {
    schema: {
      body: UserSchema,
      response: { 201: UserSchema }
    }
  }, async (request, reply) => {
    const user = await db.users.create(request.body);
    reply.status(201).send(user);
  });
};
ts
import { FastifyPluginAsync } from 'fastify';
import { Type, Static } from '@sinclair/typebox';

const UserSchema = Type.Object({
  name: Type.String(),
  email: Type.String({ format: 'email' }),
});

type User = Static<typeof UserSchema>;

const routes: FastifyPluginAsync = async (app) => {
  app.post<{ Body: User }>('/', {
    schema: {
      body: UserSchema,
      response: { 201: UserSchema }
    }
  }, async (request, reply) => {
    const user = await db.users.create(request.body);
    reply.status(201).send(user);
  });
};

Plugins

插件

ts
import fp from 'fastify-plugin';

// Custom plugin with fastify-plugin
export default fp(async (app) => {
  app.decorate('db', db);
  app.addHook('onRequest', async (request) => {
    request.startTime = Date.now();
  });
});

app.register(myPlugin);
ts
import fp from 'fastify-plugin';

// 基于 fastify-plugin 实现的自定义插件
export default fp(async (app) => {
  app.decorate('db', db);
  app.addHook('onRequest', async (request) => {
    request.startTime = Date.now();
  });
});

app.register(myPlugin);

Error Handling

错误处理

ts
import { FastifyError, FastifyReply, FastifyRequest } from 'fastify';

export class AppError extends Error {
  constructor(public statusCode: number, message: string) {
    super(message);
  }
}

app.setErrorHandler((error: FastifyError, request, reply) => {
  request.log.error({ err: error }, 'Request error');

  if (error.validation) {
    return reply.status(400).send({ error: 'Validation failed', details: error.validation });
  }

  return reply.status(error.statusCode || 500).send({ error: error.message });
});
ts
import { FastifyError, FastifyReply, FastifyRequest } from 'fastify';

export class AppError extends Error {
  constructor(public statusCode: number, message: string) {
    super(message);
  }
}

app.setErrorHandler((error: FastifyError, request, reply) => {
  request.log.error({ err: error }, 'Request error');

  if (error.validation) {
    return reply.status(400).send({ error: 'Validation failed', details: error.validation });
  }

  return reply.status(error.statusCode || 500).send({ error: error.message });
});

Health Checks

健康检查

ts
app.get('/health', async () => ({ status: 'healthy' }));

app.get('/ready', async (request, reply) => {
  try {
    await app.pg.query('SELECT 1');
    return { status: 'ready', database: 'connected' };
  } catch (error) {
    reply.status(503);
    return { status: 'not ready' };
  }
});
ts
app.get('/health', async () => ({ status: 'healthy' }));

app.get('/ready', async (request, reply) => {
  try {
    await app.pg.query('SELECT 1');
    return { status: 'ready', database: 'connected' };
  } catch (error) {
    reply.status(503);
    return { status: 'not ready' };
  }
});

Testing

测试

ts
import { test } from 'vitest';
import { buildApp } from '../src/app';

test('GET /api/users returns users', async () => {
  const app = await buildApp();

  const response = await app.inject({
    method: 'GET',
    url: '/api/users',
  });

  expect(response.statusCode).toBe(200);
  await app.close();
});
ts
import { test } from 'vitest';
import { buildApp } from '../src/app';

test('GET /api/users returns users', async () => {
  const app = await buildApp();

  const response = await app.inject({
    method: 'GET',
    url: '/api/users',
  });

  expect(response.statusCode).toBe(200);
  await app.close();
});

WebSocket Setup

WebSocket 配置

ts
import websocket from '@fastify/websocket';

await app.register(websocket);

app.get('/ws', { websocket: true }, (socket, req) => {
  socket.on('message', (data) => {
    const message = JSON.parse(data.toString());
    handleMessage(socket, message);
  });
});
ts
import websocket from '@fastify/websocket';

await app.register(websocket);

app.get('/ws', { websocket: true }, (socket, req) => {
  socket.on('message', (data) => {
    const message = JSON.parse(data.toString());
    handleMessage(socket, message);
  });
});

When NOT to Use This Skill

不适用该技能的场景

  • Enterprise Architecture - Use NestJS for DI and modular design
  • Simple CRUD APIs - Use Express if performance is not critical
  • Edge Runtimes - Use Hono for Cloudflare Workers
  • Deno Projects - Use Oak or Fresh instead
  • 企业级架构:需要 DI 和模块化设计请使用 NestJS
  • 简单 CRUD API:性能要求不高时请使用 Express
  • Edge 运行时:Cloudflare Workers 场景请使用 Hono
  • Deno 项目:请改用 Oak 或 Fresh

Anti-Patterns

反模式

Anti-PatternWhy It's BadCorrect Approach
Not using schema validationLoses performance advantageDefine schemas for routes
Missing
fastify-plugin
wrapper
Encapsulation issuesUse
fp()
for shared plugins
Using
app.listen()
in tests
Slow, port conflictsUse
app.inject()
Ignoring response schemaSlower serializationDefine response schemas
反模式弊端正确做法
不使用 schema 验证丢失性能优势为路由定义 schema
未加
fastify-plugin
包装
出现封装问题共享插件使用
fp()
包装
测试中使用
app.listen()
速度慢、端口冲突使用
app.inject()
忽略响应 schema降低序列化速度定义响应 schema

Quick Troubleshooting

快速故障排查

IssueLikely CauseSolution
"Cannot call reply.send twice"Multiple sendsReturn after first
reply.send()
Plugin not accessibleMissing fastify-pluginWrap with
fp()
Schema not workingNot registeredAdd
schema: {}
to route options
Type errors with schemasProvider missingUse
withTypeProvider<TypeBoxTypeProvider>()
问题可能原因解决方案
"Cannot call reply.send twice"多次调用 send 方法首次调用
reply.send()
后添加 return
插件无法访问缺少 fastify-plugin 包装
fp()
包装插件
Schema 不生效未注册在路由配置中添加
schema: {}
Schema 类型报错缺少类型提供者使用
withTypeProvider<TypeBoxTypeProvider>()

Production Checklist

生产环境检查清单

  • TypeBox schema validation
  • Helmet security headers
  • Rate limiting configured
  • Structured logging (Pino)
  • Request ID tracing
  • Custom error handler
  • Health/readiness endpoints
  • Graceful shutdown
  • Database connection pooling
  • TypeBox schema 验证
  • Helmet 安全头配置
  • 限流规则配置
  • 结构化日志(Pino)
  • 请求 ID 链路追踪
  • 自定义错误处理器
  • 健康/就绪检查接口
  • 优雅停机逻辑
  • 数据库连接池配置

Monitoring Metrics

监控指标

MetricTarget
Response time (p99)< 50ms
Error rate (5xx)< 0.1%
Request throughput> 10k/s
指标目标值
响应时间(p99)< 50ms
错误率(5xx)< 0.1%
请求吞吐量> 10k/s

Reference Documentation

参考文档

  • Schema Validation
  • Plugins
  • Schema 验证
  • 插件