Skip to content

Latest commit

 

History

History
460 lines (348 loc) · 10.4 KB

File metadata and controls

460 lines (348 loc) · 10.4 KB

Plugin System

Comprehensive guide to the Qwe Framework plugin system, including built-in plugins and custom plugin development.

Table of Contents

Overview

The Qwe Framework plugin system provides a powerful way to extend application functionality through reusable components. Plugins follow a standardized interface and can register middleware, modify application behavior, and integrate with the framework's core features.

Key Features

  • 🔌 Standardized Interface: All plugins implement the same contract
  • ⚡ Easy Integration: Simple registration with app.plugin()
  • 🔧 Middleware Support: Plugins can register middleware functions
  • 🎛️ Configurable: Options-based configuration for flexibility
  • 🏗️ Extensible: Create custom plugins for specific needs

Plugin Architecture

classDiagram
    class QwePlugin {
        <<interface>>
        +string name
        +string? version
        +install(app: QweApp): void
    }
    
    class BasePlugin {
        <<abstract>>
        +string name
        +string? version
        +install(app: QweApp): void
    }
    
    class CorsPlugin {
        +options: CorsOptions
        +install(app: QweApp): void
    }
    
    class LoggerPlugin {
        +options: LoggerOptions
        +install(app: QweApp): void
    }
    
    class RateLimiterPlugin {
        +options: RateLimiterOptions
        +install(app: QweApp): void
    }
    
    QwePlugin <|.. BasePlugin
    BasePlugin <|-- CorsPlugin
    BasePlugin <|-- LoggerPlugin
    BasePlugin <|-- RateLimiterPlugin
Loading

Built-in Plugins

CORS Plugin

Cross-Origin Resource Sharing support:

import { cors } from 'qwe-framework';

// Basic usage
app.plugin(cors());

// With options
app.plugin(cors({
  origin: ['https://example.com', 'https://app.example.com'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  credentials: true
}));

Logger Plugin

Request logging with multiple formats:

import { logger } from 'qwe-framework';

// Development format with colors
app.plugin(logger({ format: 'dev' }));

// Apache combined format
app.plugin(logger({ format: 'combined' }));

Log Formats:

  • tiny: GET /users 200 - 15ms
  • dev: Color-coded format for development
  • combined: Apache combined format

Rate Limiter Plugin

Protect your API from abuse:

import { rateLimiter } from 'qwe-framework';

app.plugin(rateLimiter({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // Max 100 requests per window
  message: 'Too many requests, please try again later'
}));

Security Plugin

Add security headers:

import { security } from 'qwe-framework';

app.plugin(security({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      styleSrc: ["'self'", "'unsafe-inline'"]
    }
  }
}));

Headers Added:

  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • X-XSS-Protection: 1; mode=block
  • Strict-Transport-Security
  • Content-Security-Policy

Compression Plugin

Compress response data:

import { compression } from 'qwe-framework';

app.plugin(compression({
  threshold: 1024, // Only compress responses > 1KB
  level: 6 // Compression level (1-9)
}));

Body Parser Plugin

Parse request bodies:

import { bodyParser } from 'qwe-framework';

app.plugin(bodyParser({
  json: { limit: '10mb' },
  urlencoded: { limit: '1mb', extended: true }
}));

JWT Auth Plugin

Automatic JWT validation:

import { jwtAuth } from 'qwe-framework';

app.plugin(jwtAuth({
  secret: process.env.JWT_SECRET,
  algorithms: ['HS256'],
  unless: (qwe) => qwe.url.startsWith('/public')
}));

Plugin Usage

Basic Registration

import { createApp, cors, logger, rateLimiter } from 'qwe-framework';

const app = createApp();

// Register plugins in order
app.plugin(logger());
app.plugin(cors());
app.plugin(rateLimiter({ max: 100 }));

app.listen(3000);

Plugin Ordering

Plugin order matters! Recommended order:

app.plugin(logger());           // 1. Logging
app.plugin(cors());             // 2. CORS
app.plugin(security());         // 3. Security headers
app.plugin(compression());      // 4. Compression
app.plugin(bodyParser());       // 5. Body parsing
app.plugin(rateLimiter());      // 6. Rate limiting
app.plugin(jwtAuth());          // 7. Authentication

Custom Plugin Development

Basic Plugin Structure

import { BasePlugin, QweApp, QweMiddleware } from 'qwe-framework';

export interface MyPluginOptions {
  enabled?: boolean;
  message?: string;
}

export class MyPlugin extends BasePlugin {
  name = 'my-plugin';
  version = '1.0.0';

  constructor(private options: MyPluginOptions = {}) {
    super();
  }

  install(app: QweApp): void {
    const middleware: QweMiddleware = async (qwe, next) => {
      // Plugin logic here
      if (this.options.enabled !== false) {
        qwe.header('X-My-Plugin', 'active');
      }
      
      await next();
    };

    app.use(middleware);
  }
}

// Factory function
export function myPlugin(options?: MyPluginOptions): MyPlugin {
  return new MyPlugin(options);
}

Advanced Plugin Example

Request ID plugin for tracing:

import { BasePlugin, QweApp, QweMiddleware } from 'qwe-framework';
import { generateId } from 'qwe-framework';

export interface RequestIdOptions {
  header?: string;
  generator?: () => string;
}

export class RequestIdPlugin extends BasePlugin {
  name = 'request-id';
  version = '1.0.0';

  constructor(private options: RequestIdOptions = {}) {
    super();
  }

  install(app: QweApp): void {
    const headerName = this.options.header || 'X-Request-ID';
    const generator = this.options.generator || generateId;

    const middleware: QweMiddleware = async (qwe, next) => {
      const requestId = qwe.headers[headerName.toLowerCase()] || generator();
      
      // Add to context
      (qwe as any).requestId = requestId;
      
      // Add to response header
      qwe.header(headerName, requestId);
      
      await next();
    };

    app.use(middleware);
  }
}

export function requestId(options?: RequestIdOptions): RequestIdPlugin {
  return new RequestIdPlugin(options);
}

Metrics Collection Plugin

export class MetricsPlugin extends BasePlugin {
  name = 'metrics';
  version = '1.0.0';
  private metrics: Array<{
    method: string;
    url: string;
    statusCode: number;
    duration: number;
    timestamp: Date;
  }> = [];

  install(app: QweApp): void {
    const middleware: QweMiddleware = async (qwe, next) => {
      const startTime = Date.now();
      
      await next();
      
      this.metrics.push({
        method: qwe.method,
        url: qwe.url,
        statusCode: qwe._getStatusCode(),
        duration: Date.now() - startTime,
        timestamp: new Date()
      });
    };

    app.use(middleware);

    // Add metrics endpoint
    app.get('/metrics', (qwe) => {
      const total = this.metrics.length;
      const avgDuration = total > 0 
        ? this.metrics.reduce((sum, m) => sum + m.duration, 0) / total 
        : 0;

      return qwe.success('Metrics', {
        totalRequests: total,
        averageResponseTime: Math.round(avgDuration),
        recentRequests: this.metrics.slice(-10)
      });
    });
  }
}

Best Practices

1. Design Principles

// ✅ Good: Single responsibility
class LoggerPlugin extends BasePlugin {
  // Only handles logging
}

// ❌ Avoid: Multiple responsibilities
class LoggerCorsPlugin extends BasePlugin {
  // Handles both logging and CORS - too complex
}

2. Configuration

// ✅ Good: Sensible defaults
export interface MyPluginOptions {
  enabled?: boolean;        // Default: true
  level?: 'debug' | 'info'; // Default: 'info'
}

class MyPlugin extends BasePlugin {
  constructor(private options: MyPluginOptions = {}) {
    super();
    this.options.enabled = options.enabled ?? true;
    this.options.level = options.level ?? 'info';
  }
}

3. Error Handling

// ✅ Good: Graceful error handling
install(app: QweApp): void {
  const middleware: QweMiddleware = async (qwe, next) => {
    try {
      await this.doSomething(qwe);
      await next();
    } catch (error) {
      console.error(`${this.name} plugin error:`, error);
      await next(); // Don't break the chain
    }
  };
  app.use(middleware);
}

4. Performance

// ✅ Good: Efficient operations
class PerformantPlugin extends BasePlugin {
  private cache = new Map();

  install(app: QweApp): void {
    const middleware: QweMiddleware = async (qwe, next) => {
      // Fast cache lookup
      const cached = this.cache.get(qwe.url);
      if (cached) {
        qwe.header('X-Cache', 'HIT');
      }
      await next();
    };
    app.use(middleware);
  }
}

5. Testing

describe('MyPlugin', () => {
  it('should add custom header', async () => {
    const app = createApp();
    app.plugin(new MyPlugin({ header: 'X-Test' }));
    
    app.get('/test', (qwe) => qwe.success('ok'));
    
    const response = await request(app)
      .get('/test')
      .expect(200);
    
    expect(response.headers['x-test']).toBeDefined();
  });
});

Conclusion

The Qwe Framework plugin system provides a powerful and flexible way to extend application functionality. Whether using built-in plugins or creating custom ones, the standardized interface ensures consistency and maintainability.

Key Takeaways

  • Use built-in plugins for common functionality
  • Follow the plugin interface when creating custom plugins
  • Order plugins correctly for proper execution flow
  • Handle errors gracefully to maintain stability
  • Keep plugins focused on single responsibilities

Next Steps


Need help? Check the API Reference or visit our GitHub repository for examples and support.