Add Methods or Properties to Core Objects
Decorators — Extend app / request / reply
app.decorate, decorateRequest, and decorateReply attach properties and methods accessible from every handler in scope.
What you'll learn
- Use app.decorate to expose a service
- Use decorateRequest to pre-allocate per-request slots
- Avoid decorating with mutable shared state
Decorators are the official way to extend the app, request, or reply objects. They are checked at boot, scoped by the plugin tree, and friendly to the V8 hidden-class optimizer.
Decorating the App
import fp from 'fastify-plugin';
import { createClient } from './db.js';
export default fp(async (app) => {
const db = await createClient(app.config.DB_URL);
app.decorate('db', db);
app.addHook('onClose', async () => db.close());
}); Now app.db is callable from any handler in the subtree:
app.get('/posts', async () => app.db.posts.findMany()); Decorating Requests
declare module 'fastify' {
interface FastifyRequest {
user: { id: string } | null;
}
}
app.decorateRequest('user', null);
app.addHook('preHandler', async (req) => {
req.user = await verifyToken(req.headers.authorization);
}); Pre-declaring with null (or another primitive) keeps every request’s hidden class identical,
which keeps property access fast.
Avoid Mutable References
// BAD: every request shares the same array
app.decorateRequest('events', []); Reference values are shared across requests. Use a getter or initialize inside an onRequest
hook instead:
app.decorateRequest('events', null);
app.addHook('onRequest', async (req) => {
req.events = [];
}); Checking Existence
app.hasDecorator('db') and app.hasRequestDecorator('user') let dependent plugins verify
their prerequisites at boot.