Azura Logo
GitHub

Plugins System

Extend Azura with built-in and custom plugins.

Plugin Architecture

Azura's plugin system allows you to extend the framework's functionality in a modular way. Plugins can add middleware, routes, hooks, and more to your application.

typescript
import { Plugin } from '@azura/framework';

// Plugin interface
interface Plugin<API = unknown, Options = unknown> {
  name: string;
  register(app: AzuraServer, opts: Options): API;
  dependencies?: string[];
}

Each plugin has a unique name, a register function that receives the server instance and options, and an optional list of dependencies.

Built-in Plugins

Azura comes with several built-in plugins that you can use out of the box:

CORS Plugin

typescript
import { AzuraServer, cors } from '@azura/framework';

const app = new AzuraServer();

// Register the CORS plugin
app.registerPlugin(cors, {
  origin: '*', // Allow all origins
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization']
});

Compression Plugin

typescript
import { AzuraServer, compress } from '@azura/framework';

const app = new AzuraServer();

// Register the compression plugin
app.registerPlugin(compress, {
  threshold: 1024 // Only compress responses larger than 1KB
});

Rate Limiting Plugin

typescript
import { AzuraServer, rateLimit } from '@azura/framework';

const app = new AzuraServer();

// Register the rate limiting plugin
app.registerPlugin(rateLimit, {
  limit: 100, // 100 requests
  timeframe: 60000 // per minute (60000ms)
});

Static Files Plugin

typescript
import { AzuraServer, serveStatic } from '@azura/framework';

const app = new AzuraServer();

// Register the static files plugin
app.registerPlugin(serveStatic, {
  root: './public' // Serve files from the public directory
});

Metrics Plugin

typescript
import { AzuraServer, metrics, metricsEndpoint } from '@azura/framework';

const app = new AzuraServer();

// Register the metrics plugin
app.registerPlugin(metrics);

// Add a metrics endpoint
app.get('/metrics', metricsEndpoint());

Health Check Plugin

typescript
import { AzuraServer, health } from '@azura/framework';

const app = new AzuraServer();

// Register the health check plugin
app.get('/health', health());

Cache Plugin

typescript
import { AzuraServer, LRUCache } from '@azura/framework';

const app = new AzuraServer();

// Create an LRU cache
const cache = new LRUCache(1000); // Cache up to 1000 items

// Use the cache in a route
app.get('/cached-data', (req, res) => {
  const cacheKey = req.path;
  const cachedData = cache.get(cacheKey);
  
  if (cachedData) {
    return res.json(cachedData);
  }
  
  // Get fresh data
  const data = { /* ... */ };
  
  // Cache the data
  cache.set(cacheKey, data);
  
  res.json(data);
});

Creating Custom Plugins

You can create your own plugins to extend Azura's functionality:

typescript
import { AzuraServer } from '@azura/framework';

// Define a plugin
const loggerPlugin = {
  name: 'logger',
  register: (app, options = { level: 'info' }) => {
    // Add middleware to log requests
    app.use((req, res, next) => {
      const start = Date.now();
      
      // Add a listener for when the response finishes
      res.on('finish', () => {
        const duration = Date.now() - start;
        console.log(
          `[${options.level.toUpperCase()}] ${req.method} ${req.path} - ${res.statusCode} (${duration}ms)`
        );
      });
      
      next();
    });
    
    // Return an API that other plugins can use
    return {
      log: (message: string, level = options.level) => {
        console.log(`[${level.toUpperCase()}] ${message}`);
      }
    };
  }
};

// Use the plugin
const app = new AzuraServer();
const logger = app.registerPlugin(loggerPlugin, { level: 'debug' });

// Use the plugin's API
logger.log('Server started', 'info');

Plugin Dependencies

Plugins can depend on other plugins. The plugin loader will automatically load dependencies in the correct order:

typescript
import { AzuraServer } from '@azura/framework';

// Define plugins
const databasePlugin = {
  name: 'database',
  register: (app, options) => {
    // Connect to database...
    return {
      query: (sql: string) => {
        // Execute query...
        return [];
      }
    };
  }
};

const userPlugin = {
  name: 'users',
  dependencies: ['database'], // This plugin depends on the database plugin
  register: (app, options) => {
    // Get the database plugin
    const db = app.plugins.get('database');
    
    // Add routes that use the database
    app.get('/users', (req, res) => {
      const users = db.query('SELECT * FROM users');
      res.json(users);
    });
    
    return {
      getUser: (id: string) => {
        return db.query('SELECT * FROM users WHERE id = ?', [id])[0];
      }
    };
  }
};

// Register plugins
const app = new AzuraServer();
app.registerPlugin(databasePlugin, { /* database options */ });
app.registerPlugin(userPlugin);

In this example, the userPlugin depends on the databasePlugin. The plugin loader will ensure that databasePlugin is registered before userPlugin.

Plugin Loader

Azura uses a PluginLoader class to manage plugins and their dependencies:

typescript
import { AzuraServer, PluginLoader } from '@azura/framework';

// Create a server
const app = new AzuraServer();

// The server has a built-in plugin loader
const plugins = app.plugins;

// Register a plugin
const loggerApi = plugins.register('logger', loggerPlugin, { level: 'debug' });

// Get a plugin's API
const logger = plugins.get('logger');
logger.log('Hello, world!');

The plugin loader ensures that plugins are only registered once, even if they are requested multiple times by different dependencies.

Lifecycle Hooks Plugin

The lifecycle hooks plugin allows you to execute code at various stages of the request/response cycle:

typescript
import { AzuraServer, Lifecycle, HookType } from '@azura/framework';

const app = new AzuraServer();

// Get the lifecycle plugin
const lifecycle = app.plugins.get('lifecycle');

// Add hooks
lifecycle.add('onRequest', (ctx) => {
  console.log('Request received:', ctx.request.url);
});

lifecycle.add('preHandler', (ctx) => {
  console.log('Before handler execution');
});

lifecycle.add('onResponse', (ctx) => {
  console.log('Response sent:', ctx.response.statusCode);
});

// Available hook types:
// - onRequest: Triggered when a request is received
// - preParsing: Before body parsing
// - preValidation: Before input validation
// - preHandler: Before route handler execution
// - onResponse: Before sending the response
// - onError: When an error occurs