Fastify Plugins
Organize Fastify apps with encapsulated plugins, autoload, and shared decorators.
Search across all documentation pages
Organize Fastify apps with encapsulated plugins, autoload, and shared decorators.
Quick-reference recipe card - copy-paste ready.
import Fastify from "fastify";
import fp from "fastify-plugin";
import autoload from "@fastify/autoload";
import { join } from "node:path";
const app = Fastify({ logger: true });
// Shared decorator (breaks encapsulation intentionally)
await app.register(fp(async (fastify) => {
fastify.decorate("db", { query: async (sql: string) => [] });
}));
// Autoload all plugins in ./plugins and routes in ./routes
await app.register(autoload, { dir: join(import.meta.dirname, "plugins") });
await app.register(autoload, { dir: join(import.meta.dirname, "routes") });
await app.listen({ port: 3000 });When to reach for this: Any Fastify app beyond a single file. Plugins are the primary modularity unit.
src/
app.ts
plugins/
auth.ts
database.ts
routes/
users.ts
health.ts
// plugins/database.ts
import fp from "fastify-plugin";
export default fp(async (fastify) => {
const pool = { query: async (sql: string) => [{ id: 1 }] };
fastify.decorate("db", pool);
fastify.addHook("onClose", async () => {
// close pool connections
});
});
// routes/users.ts
import { FastifyPluginAsync } from "fastify";
const users: FastifyPluginAsync = async (fastify) => {
fastify.get("/", async () => {
return fastify.db.query("SELECT * FROM users");
});
};
export default users;
// app.ts
import Fastify from "fastify";
import autoload from "@fastify/autoload";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url));
const app = Fastify({ logger: true });
await app.register(autoload, { dir: join(__dirname, "plugins") });
await app.register(autoload, { dir: join(__dirname, "routes"), options: { prefix: "/api" } });What this demonstrates:
fastify-plugin wraps shared plugins for global scope@fastify/autoload discovers plugins by directory conventiononClose hook for cleanup on shutdown/api prefixregister(plugin) creates a new encapsulation contextfastify-plugin (fp) lifts a plugin to the parent scope| Pattern | Scope | Use for |
|---|---|---|
Plain register | Child scope only | Feature modules |
fastify-plugin | Parent scope | DB, auth, config |
prefix option | URL prefix | Route grouping |
onClose hook | Cleanup | Connection pools |
import "fastify";
declare module "fastify" {
interface FastifyInstance {
db: { query: (sql: string) => Promise<unknown[]> };
}
}fp. Fix: wrap with fastify-plugin.plugins/ and routes/ dirs; plugins dir loads first.await app.register(dbPlugin) before routes.onClose cleanup - connection pool leaks on SIGTERM. Fix: close pools in onClose.export default async function. Fix: follow convention.| Alternative | Use When | Don't Use When |
|---|---|---|
| Manual register in app.ts | Small apps (< 5 plugins) | Growing codebase |
| NestJS modules | Need DI, decorators, guards | Want minimal framework |
| Express Router | Express codebase | Fastify project |
| Monolith single file | Spike/prototype | Production service |
Both use register. Routes are plugins that only define endpoints. Plugins can add decorators, hooks, and child plugins.
Alphabetical by filename. Prefix files with numbers (01-database.ts) to control order if needed.
Yes. fastify.register(plugin, { prefix: "/api", dbUrl: "..." }). Access via fastify-plugin opts or closure.
Create a bare Fastify() instance, register the plugin, use inject(). No port needed.
Yes. One plugin per domain (users, orders, billing) with its own routes, hooks, and schemas.
Register with fastify.addSchema() in a schemas plugin loaded first, then $ref in route schemas.
Use tsx or compile to JS first. Autoload loads .js files from the build output in production.
Fastify plugins are lighter with no DI container. NestJS modules add providers, imports, and exports. See NestJS Basics.
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.
Reviewed by Chris St. John·Last updated Jul 18, 2026