mcpField
F
fastify-v5

fastify-v5

github.com/blockmaticDevelopment1221 installsUpdated 5w ago
Repository

About fastify-v5

A practical guide for TypeScript developers building with Fastify v5 and TypeBox. It covers type-safe routes, OpenAPI generation, plugins, hooks, and route testing with inject().

Key features

  1. 01Define request and response schemas with TypeBox
  2. 02Infer route types from schemas
  3. 03Generate OpenAPI from schemas
  4. 04Build plugins and use request hooks
  5. 05Test routes with inject()

Use cases

  1. 01Build type-safe Fastify APIs
  2. 02Generate OpenAPI specifications for routes
  3. 03Write reusable plugins and request lifecycle logic
  4. 04Run black-box route tests

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/blockmatic/basilic-skills --skill fastify-v5

After installing, type/fastify-v5in Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

references/
hooks.md
instance-config.md
openapi-integration.md
plugins.md
testing.md
templates/
hook-plugin.ts
plugin.ts
route-schema.ts
test-utils.ts

Details

From the project README

Skill: fastify

Scope

  • Applies to: Fastify v5+ with TypeBox schemas, type-safe route definitions, automatic OpenAPI generation, plugins, hooks, testing
  • Does NOT cover: Database integration (see drizzle-orm)

Assumptions

  • Fastify v5+
  • @fastify/type-provider-typebox for TypeBox type provider
  • @sinclair/typebox for schema definitions
  • TypeScript v5+ with strict mode

Principles

  • Use TypeBox schemas for route validation (params, query, body, response)
  • Use TypeBoxTypeProvider for automatic type inference from schemas
  • Define response schemas for all status codes
  • Use OpenAPI generation from schemas (via @fastify/swagger)
  • Request types (request.params, request.query, request.body) are inferred from schemas
  • Response validation happens automatically based on schema

Constraints

MUST
  • Use TypeBoxTypeProvider for type safety: fastify.withTypeProvider<TypeBoxTypeProvider>()
  • Define response schemas for all status codes
  • Use TypeBox Type.* constructors for schema definitions
SHOULD
  • Use operationId in route schemas for OpenAPI generation
  • Use tags for route organization in OpenAPI docs
  • Define error response schemas for error cases
AVOID
  • Manual type assertions (types inferred from schemas)
  • Skipping response schema definitions
  • Mixing TypeBox with other schema libraries in same route

Interactions

  • Complements drizzle-orm for database integration
  • Generates OpenAPI specs compatible with openapi-ts codegen

Patterns

Route Schema Pattern
import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync } from 'fastify'

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

const userRoutes: FastifyPluginAsync = async fastify => {
  fastify.get('/users/:id', {
    schema: {
      operationId: 'getUser',
      params: Type.Object({ id: Type.String() }),
      response: {
        200: UserSchema,
        404: Type.Object({ code: Type.String(), message: Type.String() }),
      },
    },
  }, async (request, reply) => {
    // request.params.id is typed
    const { id } = request.params
    return reply.send(user)
  })
}
Schema Types
Type.String()
Type.Number()
Type.Boolean()
Type.Object({ id: Type.String() })
Type.Array(Type.String())
Type.Optional(Type.String())
Type.Union([Type.String(), Type.Number()])

See Route Schema Template for complete example.

Instance Configuration

Configure Fastify instance with TypeBox type provider, logger, and request settings:

import type { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
import Fastify, { LogController } from 'fastify'

const fastify = Fastify({
  logger: {
    level: 'info',
    transport: process.env.NODE_ENV === 'development' ? {
      target: 'pino-pretty',
    } : undefined,
  },
  trustProxy: true,
  bodyLimit: 1048576, // 1MB
  requestTimeout: 30000,
  requestIdHeader: 'x-request-id',
  logController: new LogController({ requestIdLogLabel: 'reqId', disableRequestLogging: false }),
}).withTypeProvider<TypeBoxTypeProvider>()

See Instance Configuration for complete setup.

Plugin Development

Create reusable plugins with fastify-plugin for non-encapsulated behavior:

import type { FastifyPluginAsync } from 'fastify'
import fp from 'fastify-plugin'

const myPlugin: FastifyPluginAsync = async fastify => {
  // Plugin logic
}

export default fp(myPlugin, {
  name: 'my-plugin',
})

See Plugin Patterns and Plugin Template for examples.

Hooks (Request Lifecycle)

Use hooks to intercept requests, modify responses, or handle errors:

fastify.addHook('onRequest', async (request, reply) => {
  // Add security headers, logging, etc.
})

fastify.addHook('onResponse', async (request, reply) => {
  // Modify response, add headers, etc.
})

fastify.addHook('onError', async (request, reply, error) => {
  // Handle errors
})

See Hooks Guide and Hook Plugin Template for patterns.

Testing with inject()

Test routes using Fastify's inject() method for blackbox testing:

const response = await fastify.inject({
  method: 'GET',
  url: '/users/123',
})

expect(response.statusCode).toBe(200)
const data = JSON.parse(response.body)

See Testing Patterns and Test Utils Template for setup.

Streaming Responses

Handle streaming responses (e.g., Server-Sent Events, text streams):

fastify.post('/stream', {
  schema: {
    response: {
      200: Type.String({ description: 'Streaming response' }),
    },
  },
}, async (request, reply) => {
  reply.header('Content-Type', 'text/event-stream')
  reply.header('Cache-Control', 'no-cache')
  return reply.send(stream)
})
Common Plugins
Rate Limiting
import rateLimit from '@fastify/rate-limit'

await fastify.register(rateLimit, {
  max: 100,
  timeWindow: 60000,
  keyGenerator: request => request.ip,
})
CORS
import cors from '@fastify/cors'

await fastify.register(cors, {
  origin: (origin, callback) => {
    // Validate origin
    callback(null, true)
  },
  credentials: false,
})
Error Handler
fastify.setErrorHandler((error, request, reply) => {
  // Global error handling
  reply.status(error.statusCode ?? 500).send({
    code: 'ERROR',
    message: error.message,
  })
})
Graceful Shutdown

Handle process signals for graceful shutdown:

const shutdown = async (signal: string) => {
  fastify.log.info({ signal }, 'Shutting down')
  await fastify.close()
  process.exit(0)
}

process.on('SIGTERM', () => shutdown('SIGTERM'))
process.on('SIGINT', () => shutdown('SIGINT'))
AutoLoad Pattern

Use @fastify/autoload for automatic plugin/route discovery:

import AutoLoad from '@fastify/autoload'

await fastify.register(AutoLoad, {
  dir: path.join(__dirname, 'plugins'),
  forceESM: true,
})

References

Comments

Markdown supported