Decorators
// Instance decorator (available via fastify.decoratorName)\napp.decorate('config', {\n appName: 'My API',\n version: '1.0.0',\n});\n\n// Request decorator (available via request.decoratorName)\napp.decorateRequest('session', null);\n\napp.addHook('preHandler', async (request) => {\n request.session = { userId: 1, role: 'admin' };\n});\n\n// Reply decorator (available via reply.decoratorName)\napp.decorateReply('success', function(data, statusCode = 200) {\n this.code(statusCode).send({\n success: true,\n data,\n });\n});\n\napp.decorateReply('fail', function(error, statusCode = 400) {\n this.code(statusCode).send({\n success: false,\n error,\n });\n});\n\n// Usage\napp.get('/profile', async (request, reply) => {\n if (!request.session) {\n return reply.fail('Not authenticated', 401);\n }\n const profile = await db.findUser(request.session.userId);\n return reply.success(profile);\n});
Hook Types
// Lifecycle hooks (in order)\napp.addHook('onRequest', handler); // Before parsing\napp.addHook('preParsing', handler); // Before body parsing\napp.addHook('preValidation', handler); // Before schema validation\napp.addHook('preHandler', handler); // Before route handler\napp.addHook('preSerialization', handler); // Before response serialization\napp.addHook('onSend', handler); // Before sending response\napp.addHook('onResponse', handler); // After response sent\napp.addHook('onTimeout', handler); // On request timeout\napp.addHook('onReady', handler); // After all plugins loaded\napp.addHook('onClose', handler); // On server close\napp.addHook('onRoute', handler); // When a route is registered
Examples
const Fastify = require('fastify');
const app = Fastify({ logger: true });
// Request timing decorator
app.decorateRequest('timing', null);
app.addHook('onRequest', async (request) => {
request.timing = { start: Date.now() };
});
app.addHook('onResponse', async (request, reply) => {
if (request.timing) {
request.timing.duration = Date.now() - request.timing.start;
app.log.info({
method: request.method,
url: request.url,
duration: `\${"$"}{request.timing.duration}ms`,
});
}
});
// Custom pagination decorator
app.decorateReply('paginated', function(items, total, opts = {}) {
const page = opts.page || 1;
const limit = opts.limit || 10;
const totalPages = Math.ceil(total / limit);
this.send({
data: items,
meta: {
page,
limit,
total,
totalPages,
hasNext: page < totalPages,
hasPrev: page > 1,
},
});
});
// Usage
app.get('/items', async (request, reply) => {
const page = Number(request.query.page) || 1;
const limit = Number(request.query.limit) || 10;
const items = Array.from({ length: limit }, (_, i) => ({
id: (page - 1) * limit + i + 1,
name: `Item \${"$"}{(page - 1) * limit + i + 1}`,
}));
return reply.paginated(items, 100, { page, limit });
});
app.listen({ port: 3000 });