Plugins — Fastify's Composition Unit

Every Feature Is a Plugin

Plugins — Fastify's Composition Unit

A Fastify plugin is an async function registered on the app — it adds routes, hooks, decorators, or other plugins, all in its own scope.

4 min read Level 2/5 #fastify#plugins#composition
What you'll learn
  • Write a plugin as an async (app, opts) function
  • Register it with app.register
  • Pass options through to the plugin

In Fastify, everything composable is a plugin: routes, middleware, integrations, even your own features. A plugin is just an async function plus a register call.

A Minimal Plugin

// src/plugins/health.ts
import type { FastifyPluginAsync } from 'fastify';

const healthPlugin: FastifyPluginAsync = async (app, opts) => {
  app.get('/healthz', async () => ({ ok: true }));
  app.get('/readyz', async () => ({ ready: await db.isReady() }));
};

export default healthPlugin;

(app, opts) is the standard signature. app is a child of the parent app — anything you register on it inherits parent context but does not leak back (more on that in the encapsulation lesson).

Registering

import healthPlugin from './plugins/health.js';

await app.register(healthPlugin, {
  // anything you want — Fastify passes it as the second arg
  intervalMs: 5_000,
});

Options Are Yours

type Opts = { intervalMs?: number };

const myPlugin: FastifyPluginAsync<Opts> = async (app, opts) => {
  const interval = opts.intervalMs ?? 30_000;
  app.log.info({ interval }, 'plugin booted');
};

Why Use Plugins for Everything

Plugins encapsulate routes, hooks, and decorators into reusable units, give you per-feature test boundaries (mount one plugin into a test app), and they compose — a billing plugin can register Stripe, webhook routes, and a CRON-runner under one name.

The fastify-plugin Wrapper →