Fastify 5 best practices, API reference, and patterns for routes, plugins, hooks, validation, error handling, and TypeScript. Use when: (1) writing new Fastify routes, plugins, or hooks, (2) looking up Fastify API signatures or options, (3) debugging Fastify issues (lifecycle, encapsulation, validation, plugin timeout), (4) reviewing Fastify code for anti-patterns. Triggers: 'add a route', 'create plugin', 'Fastify hook', 'validation schema', 'Fastify error', 'setErrorHandler', 'fastify-plugin'.
<quick_reference>
Incoming Request
└─ Routing
└─ onRequest hooks
└─ preParsing hooks
└─ Content-Type Parsing
└─ preValidation hooks
└─ Schema Validation (→ 400 on failure)
└─ preHandler hooks
└─ Route Handler
└─ preSerialization hooks
└─ onSend hooks
└─ Response Sent
└─ onResponse hooks
Error at any stage → onError hooks → error handler → onSend → response → onResponse.
</quick_reference>
<anti_patterns>
Mixing async/callback in handlers — Use async OR callbacks, never both. With async, return the value; don't call reply.send() AND return.
Returning undefined from async handler — Fastify treats this as "no response yet". Return the data or call reply.send().
Using arrow functions when you need this — Arrow functions don't bind this to the Fastify instance. Use function declarations for handlers that need this.
Forgetting fastify-plugin wrapper — Without it, decorators/hooks stay scoped to the child context. Parent and sibling plugins won't see them.
Decorating with reference types directly — decorateRequest('data', {}) shares the SAME object across all requests. Use null initial + onRequest hook to assign per-request.
Sending response in onError hook — onError is read-only for logging. Use setErrorHandler() to modify error responses.
Not handling reply.send() in async hooks — Call return reply after reply.send() in async hooks to prevent "Reply already sent" errors.
Ignoring encapsulation — Decorators/hooks registered in child plugins are invisible to parents. Design your plugin tree carefully.
String concatenation in SQL from route params — Always use parameterized queries. Fastify validates input shape, not content safety.
Missing response schema — Without response schema, Fastify serializes with JSON.stringify() (slow) and may leak sensitive fields. Use fast-json-stringify via response schemas.
</anti_patterns>
<examples>Project convention: use FastifyPluginCallback + done() (avoids require-await lint errors).
import fp from "fastify-plugin";
import type { FastifyPluginCallback } from "fastify";
const myPlugin: FastifyPluginCallback = (fastify, opts, done) => {
fastify.decorate("myService", new MyService());
done();
};
export default fp(myPlugin, { name: "my-plugin" });
fastify.post<{ Body: CreateUserBody }>("/users", {
schema: {
body: {
type: "object",
required: ["email", "name"],
properties: {
email: { type: "string", format: "email" },
name: { type: "string", minLength: 1 },
},
},
response: {
201: {
type: "object",
properties: {
id: { type: "string" },
email: { type: "string" },
},
},
},
},
handler: async (request, reply) => {
const user = await createUser(request.body);
return reply.code(201).send(user);
},
});
fastify.addHook("onRequest", async (request, reply) => {
request.startTime = Date.now();
});
fastify.addHook("onResponse", async (request, reply) => {
request.log.info({ elapsed: Date.now() - request.startTime }, "request completed");
});
fastify.setErrorHandler((error, request, reply) => {
request.log.error(error);
const statusCode = error.statusCode ?? 500;
reply.code(statusCode).send({
error: statusCode >= 500 ? "Internal Server Error" : error.message,
});
});
</examples>
<references>
Load the relevant file when you need detailed API information:
npx skills add jgamaraalv/fastify-best-practices下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer