CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-fastify

Fast and low overhead web framework for Node.js with powerful plugin architecture

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

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

docs

content-parsing.md

decoration.md

error-handling.md

hooks.md

index.md

plugins.md

routing.md

schema.md

server-lifecycle.md

testing.md

tile.json