fastify-plugin
Wraps a Fastify plugin function so it breaks out of encapsulation, checks the host's Fastify version, and carries dependency/name metadata.
Repository Health
Technical Analysis
fastify-plugin is the standard way to author well-behaved plugins for the Fastify web framework. Fastify normally scopes everything a plugin registers — decorators, hooks, routes — to that plugin’s own encapsulation context, so a decorator added inside a plugin isn’t visible to sibling plugins or the parent instance. fastify-plugin wraps a plugin function and sets the internal skip-override symbol, which tells Fastify to skip creating a new encapsulation context for it, making the plugin’s decorators and hooks visible at the level it was registered.
Beyond encapsulation, fastify-plugin standardizes how plugins declare their own requirements: a semver range for the minimum Fastify version they need, a name so Fastify’s dependency graph can identify them, and lists of decorators and dependencies that must already exist before the plugin loads. Fastify validates all of this at registration time and throws a clear error if a version or dependency check fails, instead of surfacing a confusing runtime bug. It is maintained by the Fastify core team and is a near-universal dependency of the Fastify plugin ecosystem.
What You Get
- Encapsulation bypass - sets the
skip-overridesymbol so a plugin’s decorators, hooks, and routes attach at the parent scope instead of being sandboxed - Fastify version guard - accepts a semver range via the
fastifyoption (or a bare version string) and Fastify rejects registration if the host doesn’t satisfy it - Plugin naming - a
nameoption that Fastify’s internal dependency graph uses to identify the plugin for other plugins’dependencieschecks - Dependency + decorator checks -
dependenciesanddecoratorsoptions let a plugin assert that other named plugins or specific fastify/reply/request decorators are already registered - Optional re-encapsulation - an
encapsulate: trueoption keeps the plugin’s own decorators private while still validating its name and dependencies - CommonJS and ESM interop - auto-attaches a
.defaultproperty and a camelCase named property to the wrapped function so it works withrequire,import, and transpiled default exports
Common Use Cases
- Publishing a reusable Fastify plugin - wrap the plugin’s entry function with
fp()before publishing to npm so consumers’ decorators/hooks work at the expected scope - Sharing a decorator across route files - wrap an internal plugin that calls
fastify.decorate()so the decorator is visible outside that plugin’s own file - Enforcing a minimum Fastify version - pass
{ fastify: '5.x' }so misconfigured apps get an explicit registration-time error instead of a confusing runtime failure - Composing plugins with explicit dependencies - declare
dependencies: ['plugin-a']so a plugin refuses to load out of order in a larger application’s plugin tree
Under The Hood
Architecture
The entire package is a single wrapper function in index.js that takes a plugin function and an options object (or a bare version string, normalized into { fastify: options }). It validates the function and options shapes, derives an automatic plugin name via lib/getPluginName.js when none is supplied (parsing the function’s name or, for anonymous functions, extracting a filename from a captured stack trace), and stamps three well-known symbols onto the function: skip-override, fastify.display-name, and plugin-meta. Fastify’s own plugin-loading code (avvio, consumed at registration time, not part of this repo) reads those symbols to decide whether to create a new encapsulation context and to run version/dependency validation. A small lib/toCamelCase.js helper additionally exposes the plugin under a camelCase property matching its name, to support ergonomic named imports in TypeScript/ESM consumers. There is no internal layering beyond this — it is intentionally a thin, single-purpose annotation layer over a function.
Tech Stack
The runtime code has zero production dependencies and targets plain CommonJS Node.js, with a TypeScript declaration file (types/index.d.ts) hand-written against Fastify’s own plugin types (FastifyPluginCallback, FastifyPluginAsync) for typed consumers. Development tooling includes fastify itself and @fastify/type-provider-typebox as devDependencies for integration-style tests, neostandard/eslint for linting, c8 for coverage-enforced test running via Node’s built-in node:test runner, tstyche for type-level test assertions against the .d.ts file, and proxyquire for isolating the stack-trace-based name-extraction logic in tests. CI is delegated to a shared reusable workflow (fastify/workflows) rather than a bespoke pipeline.
Code Quality
Tests live under test/ and are extensive relative to the package’s small surface area, covering the callback and async plugin forms, error paths for non-function input and malformed options, the auto-naming fallback via stack-trace parsing, ESM/bundler interop, and composite/nested plugin scenarios. c8 --100 enforces full statement coverage on every run, and tstyche separately type-checks the declaration file against example .tst.ts fixtures, so both the runtime behavior and the public TypeScript types are continuously verified. Error handling is explicit — invalid inputs throw TypeError with descriptive messages rather than failing silently — and the codebase follows a consistent, lint-enforced style throughout.
What Makes It Unique
Rather than solving encapsulation with a configuration flag or a separate API, fastify-plugin’s approach is to attach behavior to the plugin function itself via symbols that Fastify’s core loader already understands, which keeps the mental model simple: any plugin function can opt out of encapsulation by passing through this one wrapper. Combining that with declarative version and dependency metadata — checked automatically at registration rather than left to documentation or runtime errors — is what makes it close to a de facto requirement for any plugin published into the Fastify ecosystem, despite being a tiny, dependency-free package.
Used by 8 apps in this directory
Activepieces
Automation · AI Assistants
Open-source AI automation platform that converts 280+ workflow integrations into MCP servers for LLMs, with no-code builders and TypeScript extensibility.
Dittofeed
Marketing · Automation
Open-source omni-channel customer engagement platform for automating transactional and marketing messages via email, SMS, WhatsApp, Slack, and mobile push.
GraphQL Hive
Developer Tools · Devops · Monitoring
Open-source GraphQL schema registry and observability platform with breaking change detection, federation support, and CI/CD integration for teams of any size.
Infisical
Security · Devops
The open-source platform for secrets, certificates, privileged access, and AI agent security — all in one self-hostable system.
Mastra Code
AI Code Assistants
"A coding agent that never compacts" — a terminal-based AI coding agent built on the Mastra framework, with Observational Memory instead of context compaction, multi-model support, and OAuth login for Claude Max or ChatGPT Plus.
nao
AI Development · Analytics
Build and deploy an open-source analytics agent that understands your data warehouse and answers business questions in plain English.
optio
AI Agents · AI Code Assistants
Self-hosted AI agent workflow orchestration that runs on your Kubernetes cluster — from ticket intake to squash-merged PR, entirely within your infrastructure.
Scalar
Developer Tools
Beautiful, interactive OpenAPI documentation with a built-in offline-first API client and multi-language code generation — all in one open-source platform.