Fastify
Fastify: high-performance Node.js web framework with schema-based validation, logging, plugin architecture, and TypeScript support
You are an expert in building APIs with Fastify. ## Key Points - Always define JSON Schema for request validation and response serialization — it provides runtime safety and improves serialization performance by 2-3x over `JSON.stringify`. - Use the plugin system for modularity; register route groups with prefixes and keep business logic in encapsulated plugins. - Use `fastify-plugin` (fp) only for plugins that must share state across the application (database connections, auth decorators), not for route plugins. - Forgetting that plugins are encapsulated by default — decorators registered inside a plugin are not visible to sibling plugins unless wrapped with `fastify-plugin`. - Not awaiting `fastify.ready()` before accessing decorated properties in tests, leading to undefined references during integration testing. ## Quick Example ```bash npm init -y npm install fastify npm install -D typescript @types/node tsx ``` ```bash npm install @sinclair/typebox @fastify/type-provider-typebox ```
skilldb get api-frameworks-skills/FastifyFull skill: 245 linesFastify — API Framework
You are an expert in building APIs with Fastify.
Core Philosophy
Overview
Fastify is a high-performance Node.js web framework focused on developer experience and low overhead. It provides built-in JSON Schema validation, structured logging via Pino, a powerful plugin system with proper encapsulation, and first-class TypeScript support. Fastify consistently benchmarks among the fastest Node.js frameworks while offering a rich feature set for production APIs.
Setup & Configuration
npm init -y
npm install fastify
npm install -D typescript @types/node tsx
Basic server (src/server.ts):
import Fastify from 'fastify';
const fastify = Fastify({
logger: true, // Pino logger enabled by default
});
fastify.get('/', async (request, reply) => {
return { status: 'ok' };
});
fastify.listen({ port: 3000, host: '0.0.0.0' }, (err, address) => {
if (err) {
fastify.log.error(err);
process.exit(1);
}
fastify.log.info(`Server listening at ${address}`);
});
TypeScript configuration (tsconfig.json):
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"outDir": "dist",
"declaration": true
},
"include": ["src"]
}
Core Patterns
Routing with JSON Schema Validation
Fastify uses JSON Schema for request/response validation and serialization, providing both runtime safety and automatic documentation.
import { FastifyInstance, FastifyRequest } from 'fastify';
const createUserSchema = {
body: {
type: 'object',
required: ['name', 'email'],
properties: {
name: { type: 'string', minLength: 1 },
email: { type: 'string', format: 'email' },
age: { type: 'integer', minimum: 0 },
},
additionalProperties: false,
},
response: {
201: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
email: { type: 'string' },
},
},
},
} as const;
fastify.post('/users', { schema: createUserSchema }, async (request, reply) => {
const { name, email, age } = request.body as { name: string; email: string; age?: number };
const user = { id: crypto.randomUUID(), name, email };
reply.status(201).send(user);
});
Type-Safe Routes with TypeProvider
Use @fastify/type-provider-typebox or @fastify/type-provider-json-schema-to-ts for full type inference from schemas.
npm install @sinclair/typebox @fastify/type-provider-typebox
import { Type, Static } from '@sinclair/typebox';
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox';
const UserBody = Type.Object({
name: Type.String(),
email: Type.String({ format: 'email' }),
});
const app = Fastify().withTypeProvider<TypeBoxTypeProvider>();
app.post('/users', {
schema: { body: UserBody },
}, async (request, reply) => {
// request.body is fully typed as { name: string; email: string }
const { name, email } = request.body;
return { id: crypto.randomUUID(), name, email };
});
Plugin Architecture
Plugins provide encapsulation — decorators and hooks registered in a plugin are scoped to that plugin and its children.
import fp from 'fastify-plugin';
import { FastifyInstance } from 'fastify';
// Encapsulated plugin (scoped to its context)
async function usersRoutes(fastify: FastifyInstance) {
fastify.get('/users', async () => {
return fastify.db.getUsers(); // uses decorated 'db'
});
fastify.get('/users/:id', async (request) => {
const { id } = request.params as { id: string };
return fastify.db.getUserById(id);
});
}
// Shared plugin using fastify-plugin (breaks encapsulation intentionally)
const dbPlugin = fp(async (fastify: FastifyInstance) => {
const db = await connectToDatabase();
fastify.decorate('db', db);
fastify.addHook('onClose', async () => {
await db.close();
});
});
// Register plugins
fastify.register(dbPlugin);
fastify.register(usersRoutes, { prefix: '/api/v1' });
Hooks and Middleware
// Lifecycle hooks
fastify.addHook('onRequest', async (request, reply) => {
request.startTime = Date.now();
});
fastify.addHook('preHandler', async (request, reply) => {
const token = request.headers.authorization?.replace('Bearer ', '');
if (!token) {
reply.status(401).send({ error: 'Unauthorized' });
return;
}
request.user = await verifyToken(token);
});
fastify.addHook('onResponse', async (request, reply) => {
const duration = Date.now() - request.startTime;
request.log.info({ duration, statusCode: reply.statusCode }, 'request completed');
});
// Error handler
fastify.setErrorHandler(async (error, request, reply) => {
if (error.validation) {
reply.status(400).send({
error: 'Validation Error',
details: error.validation,
});
return;
}
request.log.error(error);
reply.status(500).send({ error: 'Internal Server Error' });
});
Decorators
// Decorate the Fastify instance
fastify.decorate('config', {
dbUrl: process.env.DATABASE_URL,
jwtSecret: process.env.JWT_SECRET,
});
// Decorate the request object
fastify.decorateRequest('user', null);
// TypeScript augmentation
declare module 'fastify' {
interface FastifyInstance {
config: { dbUrl: string; jwtSecret: string };
}
interface FastifyRequest {
user: { id: string; role: string } | null;
}
}
Best Practices
- Always define JSON Schema for request validation and response serialization — it provides runtime safety and improves serialization performance by 2-3x over
JSON.stringify. - Use the plugin system for modularity; register route groups with prefixes and keep business logic in encapsulated plugins.
- Use
fastify-plugin(fp) only for plugins that must share state across the application (database connections, auth decorators), not for route plugins.
Common Pitfalls
- Forgetting that plugins are encapsulated by default — decorators registered inside a plugin are not visible to sibling plugins unless wrapped with
fastify-plugin. - Not awaiting
fastify.ready()before accessing decorated properties in tests, leading to undefined references during integration testing.
Anti-Patterns
Over-engineering for hypothetical requirements. Building for scenarios that may never materialize adds complexity without value. Solve the problem in front of you first.
Ignoring the existing ecosystem. Reinventing functionality that mature libraries already provide wastes time and introduces risk.
Premature abstraction. Creating elaborate frameworks before having enough concrete cases to know what the abstraction should look like produces the wrong abstraction.
Neglecting error handling at system boundaries. Internal code can trust its inputs, but boundaries with external systems require defensive validation.
Skipping documentation. What is obvious to you today will not be obvious to your colleague next month or to you next year.
Install this skill directly: skilldb add api-frameworks-skills
Related Skills
Elysia
"Elysia: Bun-native web framework with type-safe routing, TypeBox validation, plugins, Eden treaty client, lifecycle hooks, and Swagger documentation"
Express.js
"Express.js with TypeScript: routing, middleware patterns, error handling, validation, authentication, static files, CORS, and production-ready configuration"
Apollo GraphQL
"Apollo GraphQL: schema design, resolvers, Apollo Server, Apollo Client with React, useQuery/useMutation, caching strategies, subscriptions, and codegen"
Hono
"Hono: ultra-fast web framework for edge, serverless, and Node.js — middleware, routing, Zod validation, JWT, CORS, RPC client, and JSX support"
Koa
Koa: lightweight Node.js framework by the Express team with async/await middleware, context object, and composable architecture
NestJS
NestJS: progressive Node.js framework with decorators, dependency injection, modules, guards, pipes, and interceptors for scalable APIs