Fast and low overhead web framework for Node.js with powerful plugin architecture
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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 });