Decorator undefined in sibling Fastify plugin after register()
0 reputation · 08 Mar 2022, 15:14 UTC
0 reputation · 08 Mar 2022, 15:14 UTC
Make a decorator added by one Fastify plugin visible to a sibling plugin registered later, without forcing the provider into the parent scope.
Fastify evaluates encapsulate at registration time. With encapsulation enabled, decorators remain isolated inside the child plugin and are undefined in siblings. With encapsulation disabled, the decorator leaks into the parent instance. The fastify-plugin wrapper bypasses encapsulation entirely. autoload registers files in lexicographic order and provides no declarative dependency mechanism, so file additions or renames can change which decorator loads first.
Assuming Fastify v4 or v5, which registration pattern keeps the decorator scoped while still allowing a later consumer plugin to read it?
34775 reputation · 09 Mar 2022, 02:17 UTC
1. Does encapsulate: false on the provider guarantee visibility to subsequently registered siblings? – Yes. Setting encapsulate: false (or wrapping the plugin with fastify-plugin, which internally sets encapsulate: false) attaches the decorator to the parent instance. Any plugin registered after it can read the decorator via fastify[decoratorName].
2. Can a consumer read a decorator from an encapsulated provider without fastify‑plugin? – No. An encapsulated provider isolates its decorators to its own child instance. A consumer, even if encapsulated, inherits only the parent’s decorators, not those of the provider’s child.
3. Does autoload filesystem sort order make this behavior non‑repeatable across operating systems? – Yes. fastify-autoload registers files in the order returned by fs.readdir, which is OS‑dependent. Relying on lexicographic order is fragile; explicit registration is required for deterministic dependency resolution.
Fastify creates a new instance for each encapsulated plugin. Decorators added to that instance are invisible to other plugins unless the instance is promoted to the parent scope. The canonical way to expose a decorator to the parent without leaking it into the global namespace of every plugin is to wrap the provider with fastify-plugin or set encapsulate: false.
encapsulate: false or fastify-plugin attaches a decorator to the parent instance.register calls are deterministic.fastify-plugin (or set encapsulate: false)
const fp = require('fastify-plugin');
function provider(fastify, opts, done) {
fastify.decorate('myDecor', { value: 42 });
done();
}
module.exports = fp(provider); // exposes decorator to parent
await fastify.register(require('./provider'));
await fastify.register(require('./consumer'));
module.exports = async function (fastify, opts) {
// fastify.myDecor is available because the provider exposed it to the parent
console.log('Decorator value:', fastify.myDecor.value);
};
fastify-autoload, explicitly set the order:
await fastify.register(require('fastify-autoload'), {
dir: path.join(__dirname, 'plugins'),
ignore: ['consumer.js'] // register consumer after provider
});
await fastify.register(require('./plugins/consumer'));
If the provider plugin is already wrapped with fastify-plugin and you still observe the decorator as undefined, check whether the consumer plugin is being registered inside a fastify-plugin wrapper that sets encapsulate: true and is executed before the provider. In that case, the consumer’s instance will not see the parent decorator until after the provider registers. Adjust the registration order or remove the encapsulation wrapper.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.