Fastify's powerful plugin architecture for extending functionality with encapsulation and dependency management.
Register plugins to extend Fastify functionality with proper encapsulation.
/**
* Register a plugin with optional configuration
* @param plugin - Plugin function (callback or async)
* @param options - Plugin configuration options
* @returns FastifyInstance for method chaining
*/
register(plugin: FastifyPlugin, options?: FastifyPluginOptions): FastifyInstance;/**
* Callback-style plugin function
*/
type FastifyPluginCallback = (
fastify: FastifyInstance,
options: FastifyPluginOptions,
done: (err?: Error) => void
) => void;
/**
* Async plugin function
*/
type FastifyPluginAsync = (
fastify: FastifyInstance,
options: FastifyPluginOptions
) => Promise<void>;
/**
* Union type for all plugin types
*/
type FastifyPlugin = FastifyPluginCallback | FastifyPluginAsync;
/**
* Plugin options interface
*/
interface FastifyPluginOptions {
prefix?: string;
logLevel?: LogLevel;
logSerializers?: object;
[key: string]: any;
}Usage Examples:
const fastify = require('fastify')();
// Register a simple plugin
await fastify.register(async function (fastify, options) {
fastify.get('/plugin-route', async (request, reply) => {
return { message: 'Hello from plugin!' };
});
});
// Register plugin with options
await fastify.register(async function (fastify, options) {
fastify.get(`${options.prefix}/hello`, async (request, reply) => {
return { greeting: options.greeting };
});
}, {
prefix: '/v1',
greeting: 'Hello World'
});
// Register external plugin
await fastify.register(require('@fastify/cors'), {
origin: ['http://localhost:3000']
});
// Register plugin with specific prefix
await fastify.register(async function (fastify, options) {
fastify.get('/users', async () => ({ users: [] }));
fastify.post('/users', async () => ({ created: true }));
}, { prefix: '/api/v1' });Control plugin loading and execution flow.
/**
* Execute callback after current plugin context is loaded
* @param afterListener - Callback to execute after plugins load
* @returns FastifyInstance and Promise for method chaining
*/
after(): FastifyInstance & Promise<undefined>;
after(afterListener: (err: Error | null) => void): FastifyInstance;Usage Examples:
// Wait for plugins to load before continuing
await fastify
.register(pluginA)
.register(pluginB)
.after();
console.log('All plugins loaded');
// Callback style
fastify
.register(pluginA)
.register(pluginB)
.after((err) => {
if (err) {
console.error('Plugin loading failed:', err);
return;
}
console.log('All plugins loaded');
});
// Chain additional operations after plugins
await fastify
.register(databasePlugin)
.after()
.register(routesPlugin); // This runs after database plugin is readyGet information about registered plugins.
/**
* Check if a plugin is registered
* @param name - Plugin name to check
* @returns Boolean indicating if plugin is registered
*/
hasPlugin(name: string): boolean;
/**
* Get formatted list of all registered plugins
* @returns String representation of plugin tree
*/
printPlugins(): string;Usage Examples:
// Check if plugin is registered
const hasCors = fastify.hasPlugin('@fastify/cors');
console.log('CORS plugin registered:', hasCors);
// Print plugin tree
console.log(fastify.printPlugins());
/* Output:
└── root (fastify)
├── @fastify/cors
└── my-plugin
└── sub-plugin
*/Understanding plugin scope and encapsulation.
interface FastifyInstance {
pluginName: string; // Current plugin name chain (getter)
prefix: string; // Current route prefix (getter)
}Plugin Encapsulation Examples:
// Parent context
await fastify.register(async function parentPlugin(fastify, options) {
// This decorator is available to child plugins
fastify.decorate('parentHelper', function() {
return 'from parent';
});
await fastify.register(async function childPlugin(fastify, options) {
// Child has access to parent decorators
console.log(fastify.parentHelper()); // "from parent"
// This decorator is only available within child context
fastify.decorate('childHelper', function() {
return 'from child';
});
fastify.get('/child-route', async (request, reply) => {
return {
parent: fastify.parentHelper(),
child: fastify.childHelper()
};
});
});
// Parent cannot access child decorators
// console.log(fastify.childHelper()); // Error!
});
// Plugin with prefix
await fastify.register(async function apiPlugin(fastify, options) {
console.log('Plugin prefix:', fastify.prefix); // "/api"
fastify.get('/users', handler); // Route: /api/users
}, { prefix: '/api' });Setting and accessing plugin metadata.
// Plugin with metadata
async function myPlugin(fastify, options) {
fastify.get('/status', async () => ({ status: 'ok' }));
}
// Add metadata to plugin
myPlugin[Symbol.for('fastify.display-name')] = 'My Custom Plugin';
myPlugin[Symbol.for('skip-override')] = true;
await fastify.register(myPlugin);
// Plugin with dependencies
async function dependentPlugin(fastify, options) {
// This plugin requires another plugin to be loaded first
fastify.get('/dependent-route', handler);
}
dependentPlugin[Symbol.for('plugin-meta')] = {
name: 'dependent-plugin',
dependencies: ['@fastify/cors']
};Common patterns for writing plugins.
// Well-structured plugin
async function myPlugin(fastify, options) {
// Merge default options
const opts = Object.assign({
prefix: '/api',
timeout: 5000
}, options);
// Add plugin-specific decorators
fastify.decorate('myHelper', function(data) {
return processData(data, opts);
});
// Add hooks
fastify.addHook('preHandler', async (request, reply) => {
request.pluginData = { timestamp: Date.now() };
});
// Register routes
fastify.get('/my-endpoint', {
schema: {
response: {
200: {
type: 'object',
properties: {
result: { type: 'string' }
}
}
}
}
}, async (request, reply) => {
const result = fastify.myHelper(request.body);
return { result };
});
// Handle cleanup
fastify.addHook('onClose', async (instance) => {
// Cleanup resources
await cleanup();
});
}
// Plugin with TypeScript support
const myTypedPlugin: FastifyPluginAsync<{
prefix?: string;
timeout?: number;
}> = async (fastify, options) => {
const opts = {
prefix: '/api',
timeout: 5000,
...options
};
// Plugin implementation
};
module.exports = myPlugin;Available options when registering plugins.
interface RegisterOptions {
prefix?: string; // Route prefix for plugin routes
logLevel?: LogLevel; // Log level override for plugin
logSerializers?: object; // Custom log serializers
}Advanced Registration Examples:
// Plugin with custom log level
await fastify.register(debugPlugin, {
logLevel: 'debug'
});
// Plugin with route prefix
await fastify.register(apiPlugin, {
prefix: '/api/v2'
});
// Plugin with custom serializers
await fastify.register(auditPlugin, {
logSerializers: {
user: (user) => ({ id: user.id, name: user.name })
}
});
// Multiple plugins with different configs
await Promise.all([
fastify.register(corsPlugin, { origin: 'https://app.com' }),
fastify.register(rateLimit, { max: 100, timeWindow: 60000 }),
fastify.register(helmet, { contentSecurityPolicy: false })
]);Handling errors during plugin registration.
// Error handling with register
try {
await fastify.register(async function failingPlugin(fastify) {
throw new Error('Plugin failed to load');
});
} catch (err) {
console.error('Plugin registration failed:', err.message);
}
// Using after for error handling
fastify
.register(plugin1)
.register(plugin2)
.after((err) => {
if (err) {
console.error('Plugin loading failed:', err);
process.exit(1);
}
});
// Plugin timeout handling
await fastify.register(slowPlugin, {
// Plugin will timeout if it takes longer than 10 seconds
}, { timeout: 10000 });