or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

content-parsing.mddecoration.mderror-handling.mdhooks.mdindex.mdplugins.mdrouting.mdschema.mdserver-lifecycle.mdtesting.md
tile.json

plugins.mddocs/

Plugin System

Fastify's powerful plugin architecture for extending functionality with encapsulation and dependency management.

Capabilities

Plugin Registration

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' });

Plugin Execution Control

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 ready

Plugin Information

Get 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
*/

Plugin Context and Encapsulation

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' });

Plugin Metadata

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']
};

Plugin Best Practices

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;

Plugin Registration Options

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 })
]);

Plugin Error Handling

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 });