Decorators — Extend app / request / reply

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.

4 min read Level 2/5 #fastify#decorators#services
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.

Route Prefixes →