install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/claude-code/node-fastify-patterns" ~/.claude/skills/intense-visions-harness-engineering-node-fastify-patterns && rm -rf "$T"
manifest:
agents/skills/claude-code/node-fastify-patterns/SKILL.mdsource content
Node.js Fastify Patterns
Build performant APIs with Fastify using schema validation, plugins, decorators, and hooks
When to Use
- Building high-performance REST APIs
- Needing built-in request/response validation with JSON Schema
- Using a plugin system for modular architecture
- When Express performance is insufficient
Instructions
- Basic Fastify server:
import Fastify from 'fastify'; const fastify = Fastify({ logger: true }); fastify.get('/health', async () => ({ status: 'ok' })); await fastify.listen({ port: 3000, host: '0.0.0.0' });
- Schema-based validation (auto-generates docs and validates):
const createUserSchema = { body: { type: 'object', required: ['name', 'email'], properties: { name: { type: 'string', minLength: 1 }, email: { type: 'string', format: 'email' }, }, }, 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 user = await createUser(request.body); reply.status(201).send(user); });
- Type-safe routes with TypeBox:
import { Type, Static } from '@sinclair/typebox'; const CreateUserBody = Type.Object({ name: Type.String({ minLength: 1 }), email: Type.String({ format: 'email' }), }); type CreateUserBody = Static<typeof CreateUserBody>; fastify.post<{ Body: CreateUserBody }>( '/users', { schema: { body: CreateUserBody }, }, async (request) => { const { name, email } = request.body; // Fully typed return createUser({ name, email }); } );
- Plugins for modular architecture:
import fp from 'fastify-plugin'; // db.ts — database plugin export default fp(async (fastify) => { const db = await connectDatabase(); fastify.decorate('db', db); fastify.addHook('onClose', async () => { await db.disconnect(); }); }); // Register plugins await fastify.register(import('./plugins/db')); await fastify.register(import('./routes/users'), { prefix: '/api/users' });
- Route plugins:
// routes/users.ts import { FastifyPluginAsync } from 'fastify'; const userRoutes: FastifyPluginAsync = async (fastify) => { fastify.get('/', async () => { return fastify.db.user.findMany(); }); fastify.get('/:id', async (request) => { const { id } = request.params as { id: string }; const user = await fastify.db.user.findUnique({ where: { id } }); if (!user) throw fastify.httpErrors.notFound('User not found'); return user; }); }; export default userRoutes;
- Hooks for cross-cutting concerns:
// Authentication hook fastify.addHook('preHandler', async (request, reply) => { const token = request.headers.authorization?.replace('Bearer ', ''); if (!token) throw fastify.httpErrors.unauthorized(); request.user = await verifyToken(token); }); // Logging hook fastify.addHook('onResponse', async (request, reply) => { request.log.info({ url: request.url, method: request.method, statusCode: reply.statusCode, duration: reply.elapsedTime, }); });
- Error handling:
fastify.setErrorHandler((error, request, reply) => { request.log.error(error); if (error.validation) { reply.status(400).send({ errors: error.validation }); return; } reply.status(error.statusCode ?? 500).send({ error: error.message || 'Internal Server Error', }); });
Details
Fastify is designed for speed. It uses JSON Schema for validation and serialization, achieving 2-5x higher throughput than Express for JSON APIs.
Why Fastify is faster:
- Schema-based serialization compiles JSON stringification to optimized code (using
)fast-json-stringify - Schema-based validation uses pre-compiled validators (using
)ajv - Encapsulation model avoids middleware overhead
- Built on a radix tree router (faster than Express's regex-based routing)
Plugin encapsulation: Fastify plugins run in isolated contexts. Decorators and hooks registered inside a plugin are scoped to that plugin and its children, not the entire application.
(fastify-plugin): Wrapping a plugin with fp
fp() breaks encapsulation, making decorators available to the parent scope. Use fp for plugins that add shared functionality (database, auth); use regular plugins for routes.
Trade-offs:
- JSON Schema validation is fast — but JSON Schema syntax is verbose compared to Zod
- Plugins provide clean architecture — but the encapsulation model can be confusing at first
- TypeBox provides TypeScript inference from schemas — but adds another dependency
- Fastify is faster than Express — but has a smaller ecosystem and community
Source
https://fastify.dev/docs/latest/
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.