Encapsulation — The Big Idea

Plugins Have Their Own Scope by Default

Encapsulation — The Big Idea

Hooks, decorators, and validators added inside a plugin stay inside that plugin's subtree — Fastify's encapsulation model is what keeps large apps maintainable.

5 min read Level 3/5 #fastify#plugins#encapsulation
What you'll learn
  • Recognize that nested plugins inherit from the parent
  • See that sibling plugins do not share state
  • Use fastify-plugin to break out when you must

Encapsulation is Fastify’s secret weapon. Every app.register(plugin) creates a fresh scope — hooks added inside it run only for routes registered inside it, decorators don’t leak to siblings, and schema definitions stay local.

A Picture

import Fastify from 'fastify';

const app = Fastify();

app.register(async (api) => {
  // Auth check applies to all /api/* routes
  api.addHook('preHandler', requireAuth);

  api.get('/me', getMe);
  api.get('/posts', listPosts);
}, { prefix: '/api' });

app.register(async (pub) => {
  // No auth here — sibling plugin, separate scope
  pub.get('/', landingPage);
  pub.get('/about', aboutPage);
});

await app.listen({ port: 3000 });

The preHandler hook only runs for /api/*. The public routes have no idea it exists.

Inheritance Goes Down, Not Sideways

A nested plugin sees everything its ancestors defined: decorators, hooks, validators. It can also add its own — but only its own subtree sees them.

app.decorate('db', client);                   // available everywhere
app.register(async (sub) => {
  sub.addHook('preHandler', requireAdmin);    // only inside sub
  sub.register(async (deep) => {
    // sees `db` (inherited) and the requireAdmin hook
  });
});

When You Want Sharing

Sometimes you actively want a decorator to be global — a DB client, an auth helper, a feature flag manager. Wrap that plugin in fastify-plugin, as covered in the last lesson, and Fastify hoists the decorations to the parent scope.

Why This Matters

Most “framework” complaints in Express land come from middleware order leaking across the app — one router’s body parser running for a streaming upload, an auth check missing one new route. Encapsulation makes those mistakes structurally impossible: scope is explicit, hooks travel with the routes they were registered alongside.

Request Hooks →