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.
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 →