XyPriss Plugin Development Guide

Version: 1.0.0
Last Updated: December 12, 2025
Target: XyPriss v4.2.0+

Table of Contents

  1. Introduction
  2. Plugin Architecture
  3. Getting Started
  4. Plugin API Reference
  5. Lifecycle Hooks
  6. Advanced Features
  7. Best Practices
  8. Publishing Plugins
  9. Examples

Introduction

The XyPriss Plugin System provides a powerful and flexible way to extend server functionality. Plugins can intercept requests, add routes, modify responses, handle errors, and integrate deeply with the server lifecycle.

Key Features

  • Lifecycle Hooks: Integrate at every stage of server operation
  • Imperative API: Register plugins programmatically with Plugin.exec()
  • Declarative API: Configure plugins via server options
  • Dependency Resolution: Automatic handling of plugin dependencies
  • Type Safety: Full TypeScript support with comprehensive type definitions
  • Performance: Minimal overhead with intelligent middleware ordering

Use Cases

  • Authentication and authorization systems
  • Request/response logging and monitoring
  • Rate limiting and throttling
  • Custom caching strategies
  • API versioning
  • Error tracking and reporting
  • Performance profiling
  • Process management (e.g., Prydam)

Plugin Architecture

Plugin Structure

A XyPriss plugin is a TypeScript/JavaScript object that implements the XyPrissPlugin interface:

interface XyPrissPlugin {
    // Required metadata
    name: string;
    version: string;

    // Optional metadata
    description?: string;
    author?: string;
    dependencies?: string[];

    // Lifecycle hooks
    onServerStart?: (server: UltraFastApp) => void | Promise<void>;
    onServerReady?: (server: UltraFastApp) => void | Promise<void>;
    onServerStop?: (server: UltraFastApp) => void | Promise<void>;

    // Request/Response hooks
    onRequest?: (req: Request, res: Response, next: NextFunction) => void;
    onResponse?: (req: Request, res: Response) => void;
    onError?: (
        error: Error,
        req: Request,
        res: Response,
        next: NextFunction
    ) => void;

    // Route registration
    registerRoutes?: (app: UltraFastApp) => void;

    // Middleware
    middleware?: RequestHandler | RequestHandler[];
    middlewarePriority?: "first" | "normal" | "last";
}

Plugin Lifecycle

1. Plugin Registration
   └─> Plugin.create() or Plugin.register()

2. Server Creation
   └─> createServer()

3. Plugin Initialization
   └─> Dependency resolution
   └─> onServerStart() [BLOCKING]

4. Route Registration
   └─> registerRoutes()

5. Middleware Application
   └─> middleware / onRequest / onResponse

6. Server Start
   └─> app.start()
   └─> onServerReady()

7. Request Handling
   └─> onRequest -> Route Handler -> onResponse
   └─> onError (if error occurs)

8. Server Shutdown
   └─> onServerStop()

Getting Started

Creating Your First Plugin

Step 1: Install Dependencies

npm install xypriss
# or
bun add xypriss

Step 2: Create Plugin File

// plugins/my-plugin.ts
import { Plugin } from "xypriss";

export const MyPlugin = Plugin.create({
    name: "my-plugin",
    version: "1.0.0",
    description: "My awesome XyPriss plugin",

    onServerStart(server) {
        console.log("[MyPlugin] Server starting...");
    },

    onServerReady(server) {
        console.log("[MyPlugin] Server ready!");
    },

    registerRoutes(app) {
        app.get("/plugin/status", (req, res) => {
            res.json({ status: "active", plugin: "my-plugin" });
        });
    },
});

Step 3: Register Plugin

Option A: Imperative API (Recommended)

import { createServer, Plugin } from "xypriss";
import { MyPlugin } from "./plugins/my-plugin";

// Register before server creation
Plugin.exec(MyPlugin);

const app = createServer({
    server: { port: 3000 },
});

app.start();

Option B: Declarative API

import { createServer } from "xypriss";
import { MyPlugin } from "./plugins/my-plugin";

const app = createServer({
    server: { port: 3000 },
    plugins: {
        register: [MyPlugin],
    },
});

app.start();

Plugin API Reference

Plugin.create()

Creates a new plugin instance.

Plugin.create(config: XyPrissPlugin): XyPrissPlugin

Parameters:

  • config: Plugin configuration object implementing XyPrissPlugin interface

Returns:

  • Plugin instance ready for registration

Example:

const authPlugin = Plugin.create({
    name: "auth-plugin",
    version: "1.0.0",

    onRequest(req, res, next) {
        const token = req.headers.authorization;
        if (!token) {
            return res.status(401).json({ error: "Unauthorized" });
        }
        next();
    },
});

Plugin.register()

Registers a plugin with the global plugin manager.

Plugin.register(
    plugin: XyPrissPlugin | PluginCreator,
    config?: any
): void

Parameters:

  • plugin: Plugin instance or creator function
  • config: Optional configuration object

Example:

Plugin.register(authPlugin);

// With configuration
Plugin.register(authPlugin, {
    secretKey: process.env.JWT_SECRET,
});

Plugin.exec()

Alias for Plugin.register(). Preferred for imperative plugin registration.

Plugin.exec(
    plugin: XyPrissPlugin | PluginCreator,
    config?: any
): void

Example:

Plugin.exec(
    Plugin.create({
        name: "logger",
        version: "1.0.0",
        onRequest(req, res, next) {
            console.log(`${req.method} ${req.url}`);
            next();
        },
    })
);

Plugin.get()

Retrieves a registered plugin by name.

Plugin.get(name: string): XyPrissPlugin | undefined

Parameters:

  • name: Plugin name

Returns:

  • Plugin instance or undefined if not found

Example:

const authPlugin = Plugin.get("auth-plugin");
if (authPlugin) {
    console.log("Auth plugin is registered");
}

Plugin.factory()

Creates a plugin factory function for configurable plugins.

Plugin.factory<T = any>(
    creator: (config: T) => XyPrissPlugin
): PluginCreator<T>

Parameters:

  • creator: Function that receives configuration and returns a plugin

Returns:

  • Plugin creator function

Example:

const createRateLimiter = Plugin.factory(
    (config: { maxRequests: number; windowMs: number }) => ({
        name: "rate-limiter",
        version: "1.0.0",

        onRequest(req, res, next) {
            // Rate limiting logic using config
            next();
        },
    })
);

// Use the factory
Plugin.exec(
    createRateLimiter({
        maxRequests: 100,
        windowMs: 60000,
    })
);

Lifecycle Hooks

onServerStart

Signature:

onServerStart?: (server: UltraFastApp) => void | Promise<void>

Description: Called during server initialization, BEFORE the HTTP server starts listening. This hook BLOCKS server startup until completion.

Use Cases:

  • Database connection initialization
  • External service authentication
  • Configuration validation
  • Resource allocation

Important:

  • This hook is BLOCKING - server will not start until it completes
  • Async operations are fully supported
  • Errors will prevent server startup

Example:

{
    onServerStart: async (server) => {
        // Connect to database
        await database.connect();

        // Initialize cache
        await cache.initialize();

        console.log("Plugin initialized successfully");
    };
}

onServerReady

Signature:

onServerReady?: (server: UltraFastApp) => void | Promise<void>

Description: Called AFTER the HTTP server has started and is ready to accept connections.

Use Cases:

  • Start background jobs
  • Register with service discovery
  • Send startup notifications
  • Begin health checks

Example:

{
    onServerReady: async (server) => {
        // Start background job
        setInterval(() => {
            performCleanup();
        }, 60000);

        // Register with service discovery
        await serviceRegistry.register({
            name: "my-service",
            port: server.getPort(),
        });
    };
}

onServerStop

Signature:

onServerStop?: (server: UltraFastApp) => void | Promise<void>

Description: Called when the server is shutting down.

Use Cases:

  • Close database connections
  • Flush logs
  • Cleanup resources
  • Deregister from service discovery

Example:

{
    onServerStop: async (server) => {
        // Close connections
        await database.disconnect();

        // Flush logs
        await logger.flush();

        console.log("Plugin shutdown complete");
    };
}

onRequest

Signature:

onRequest?: (req: Request, res: Response, next: NextFunction) => void

Description: Called for EVERY incoming request, before route handlers.

Use Cases:

  • Request logging
  • Authentication
  • Request modification
  • Metrics collection

Example:

{
    onRequest(req, res, next) {
        // Add request ID
        req.id = generateId();

        // Log request
        console.log(`[${req.id}] ${req.method} ${req.url}`);

        // Add timing
        req.startTime = Date.now();

        next();
    }
}

onResponse

Signature:

onResponse?: (req: Request, res: Response) => void

Description: Called AFTER the response has been sent to the client.

Use Cases:

  • Response logging
  • Metrics collection
  • Cleanup operations
  • Audit logging

Example:

{
    onResponse(req, res) {
        const duration = Date.now() - req.startTime;
        console.log(`[${req.id}] ${res.statusCode} ${duration}ms`);

        // Send metrics
        metrics.record({
            method: req.method,
            path: req.url,
            status: res.statusCode,
            duration
        });
    }
}

onError

Signature:

onError?: (error: Error, req: Request, res: Response, next: NextFunction) => void

Description: Called when an error occurs during request processing.

Use Cases:

  • Error logging
  • Error reporting to external services
  • Custom error responses
  • Error recovery

Example:

{
    onError(error, req, res, next) {
        // Log error
        console.error(`[${req.id}] Error:`, error);

        // Send to error tracking service
        errorTracker.captureException(error, {
            request: {
                method: req.method,
                url: req.url,
                headers: req.headers
            }
        });

        // Send custom error response
        if (!res.headersSent) {
            res.status(500).json({
                error: 'Internal Server Error',
                requestId: req.id
            });
        }
    }
}

registerRoutes

Signature:

registerRoutes?: (app: UltraFastApp) => void

Description: Called during server initialization to register plugin routes.

Use Cases:

  • Add API endpoints
  • Health check endpoints
  • Admin interfaces
  • Webhook handlers

Example:

{
    registerRoutes(app) {
        // Health check
        app.get('/health', (req, res) => {
            res.json({ status: 'healthy' });
        });

        // Plugin API
        app.get('/api/plugin/stats', (req, res) => {
            res.json(getStats());
        });

        // Admin endpoint
        app.post('/api/plugin/config', (req, res) => {
            updateConfig(req.body);
            res.json({ success: true });
        });
    }
}

Advanced Features

Plugin Dependencies

Plugins can declare dependencies on other plugins. The plugin manager will automatically resolve dependencies and ensure correct initialization order.

const databasePlugin = Plugin.create({
    name: "database",
    version: "1.0.0",

    onServerStart: async () => {
        await database.connect();
    },
});

const authPlugin = Plugin.create({
    name: "auth",
    version: "1.0.0",
    dependencies: ["database"], // Requires database plugin

    onServerStart: async () => {
        // Database is guaranteed to be connected
        await loadUsers();
    },
});

Plugin.exec(authPlugin);
Plugin.exec(databasePlugin); // Order doesn't matter

Middleware Priority

Control when your middleware executes relative to other plugins.

{
    middleware: (req, res, next) => {
        // Your middleware logic
        next();
    },
    middlewarePriority: 'first' // 'first' | 'normal' | 'last'
}

Priority Levels:

  • first: Executes before all other middleware
  • normal: Default priority (default)
  • last: Executes after all other middleware

Multiple Middleware

Plugins can register multiple middleware functions.

{
    middleware: [
        (req, res, next) => {
            // First middleware
            next();
        },
        (req, res, next) => {
            // Second middleware
            next();
        },
    ];
}

Configuration Management

Use Plugin.factory() to create configurable plugins.

interface CacheConfig {
    ttl: number;
    maxSize: number;
    strategy: "lru" | "lfu";
}

export const createCachePlugin = Plugin.factory<CacheConfig>((config) => ({
    name: "cache-plugin",
    version: "1.0.0",

    onServerStart: async () => {
        await cache.initialize({
            ttl: config.ttl,
            maxSize: config.maxSize,
            strategy: config.strategy,
        });
    },

    onRequest(req, res, next) {
        const cached = cache.get(req.url);
        if (cached) {
            return res.json(cached);
        }
        next();
    },
}));

// Usage
Plugin.exec(
    createCachePlugin({
        ttl: 3600,
        maxSize: 1000,
        strategy: "lru",
    })
);

Best Practices

1. Error Handling

Always handle errors gracefully in your plugins.

{
    onServerStart: async (server) => {
        try {
            await externalService.connect();
        } catch (error) {
            console.error("Failed to connect to external service:", error);
            // Decide: fail fast or continue with degraded functionality
            throw error; // Prevents server startup
        }
    };
}

2. Resource Cleanup

Always clean up resources in onServerStop.

{
    onServerStart: async () => {
        this.connection = await database.connect();
    },

    onServerStop: async () => {
        if (this.connection) {
            await this.connection.close();
        }
    }
}

3. Performance

Minimize overhead in onRequest and onResponse hooks.

// Bad: Synchronous heavy operation
{
    onRequest(req, res, next) {
        const data = heavyComputation(); // Blocks request
        req.data = data;
        next();
    }
}

// Good: Async or lazy loading
{
    onRequest(req, res, next) {
        req.getData = () => heavyComputation(); // Lazy
        next();
    }
}

4. Naming Conventions

Use descriptive, unique names for your plugins.

// Bad
{
    name: "plugin";
}

// Good
{
    name: "xypriss-auth-jwt";
}
{
    name: "xypriss-logger-winston";
}
{
    name: "xypriss-cache-redis";
}

5. Versioning

Follow semantic versioning for your plugins.

{
    name: 'my-plugin',
    version: '1.2.3', // MAJOR.MINOR.PATCH
}

6. Documentation

Document your plugin's configuration and usage.

/**
 * JWT Authentication Plugin
 *
 * Provides JWT-based authentication for XyPriss applications.
 *
 * @example
 * ```typescript
 * Plugin.exec(createJWTAuth({
 *   secret: process.env.JWT_SECRET,
 *   expiresIn: '1h'
 * }));
 * ```
 */
export const createJWTAuth = Plugin.factory<JWTConfig>((config) => ({
    // Plugin implementation
}));

Publishing Plugins

Package Structure

my-xypriss-plugin/
├── src/
│   └── index.ts
├── dist/
│   └── index.js
├── package.json
├── tsconfig.json
├── README.md
└── LICENSE

package.json

{
    "name": "@yourorg/xypriss-plugin-name",
    "version": "1.0.0",
    "description": "Description of your plugin",
    "main": "dist/index.js",
    "types": "dist/index.d.ts",
    "keywords": ["xypriss", "plugin", "your-keywords"],
    "peerDependencies": {
        "xypriss": "^4.5.0"
    },
    "devDependencies": {
        "xypriss": "^4.5.11",
        "typescript": "^5.0.0"
    }
}

README.md Template

# @yourorg/xypriss-plugin-name

Description of what your plugin does.

## Installation

\`\`\`bash
npm install @yourorg/xypriss-plugin-name
\`\`\`

## Usage

\`\`\`typescript
import { Plugin } from 'xypriss';
import { MyPlugin } from '@yourorg/xypriss-plugin-name';

Plugin.exec(MyPlugin);
\`\`\`

## Configuration

| Option  | Type   | Default   | Description |
| ------- | ------ | --------- | ----------- |
| option1 | string | 'default' | Description |

## License

MIT

Publishing to npm

# Build
npm run build

# Test
npm test

# Publish
npm publish --access public

Examples

Example 1: Request Logger

import { Plugin } from "xypriss";

export const RequestLogger = Plugin.create({
    name: "request-logger",
    version: "1.0.0",
    description: "Logs all incoming requests",

    onRequest(req, res, next) {
        const start = Date.now();
        req.startTime = start;

        console.log(`--> ${req.method} ${req.url}`);
        next();
    },

    onResponse(req, res) {
        const duration = Date.now() - req.startTime;
        console.log(
            `<-- ${req.method} ${req.url} ${res.statusCode} ${duration}ms`
        );
    },
});

Example 2: API Key Authentication

import { Plugin } from "xypriss";

interface AuthConfig {
    apiKeys: string[];
    headerName?: string;
}

export const createAPIKeyAuth = Plugin.factory<AuthConfig>((config) => ({
    name: "api-key-auth",
    version: "1.0.0",

    onRequest(req, res, next) {
        const headerName = config.headerName || "x-api-key";
        const apiKey = req.headers[headerName];

        if (!apiKey || !config.apiKeys.includes(apiKey as string)) {
            return res.status(401).json({
                error: "Unauthorized",
                message: "Invalid or missing API key",
            });
        }

        next();
    },
}));

// Usage
Plugin.exec(
    createAPIKeyAuth({
        apiKeys: ["key1", "key2", "key3"],
        headerName: "x-api-key",
    })
);

Example 3: Rate Limiter

import { Plugin } from "xypriss";

interface RateLimitConfig {
    maxRequests: number;
    windowMs: number;
}

export const createRateLimiter = Plugin.factory<RateLimitConfig>((config) => {
    const requests = new Map<string, number[]>();

    return {
        name: "rate-limiter",
        version: "1.0.0",

        onRequest(req, res, next) {
            const ip = req.ip || req.connection.remoteAddress;
            const now = Date.now();

            if (!requests.has(ip)) {
                requests.set(ip, []);
            }

            const timestamps = requests.get(ip)!;
            const validTimestamps = timestamps.filter(
                (t) => now - t < config.windowMs
            );

            if (validTimestamps.length >= config.maxRequests) {
                return res.status(429).json({
                    error: "Too Many Requests",
                    retryAfter: Math.ceil(config.windowMs / 1000),
                });
            }

            validTimestamps.push(now);
            requests.set(ip, validTimestamps);

            next();
        },
    };
});

// Usage
Plugin.exec(
    createRateLimiter({
        maxRequests: 100,
        windowMs: 60000, // 1 minute
    })
);

Example 4: Health Check

import { Plugin } from "xypriss";

export const HealthCheckPlugin = Plugin.create({
    name: "health-check",
    version: "1.0.0",

    registerRoutes(app) {
        app.get("/health", (req, res) => {
            res.json({
                status: "healthy",
                uptime: process.uptime(),
                memory: process.memoryUsage(),
                timestamp: new Date().toISOString(),
            });
        });

        app.get("/ready", (req, res) => {
            // Check if all dependencies are ready
            const ready = checkDependencies();
            res.status(ready ? 200 : 503).json({
                ready,
                timestamp: new Date().toISOString(),
            });
        });
    },
});

Example 5: Error Tracker

import { Plugin } from "xypriss";

interface ErrorTrackerConfig {
    apiKey: string;
    environment: string;
}

export const createErrorTracker = Plugin.factory<ErrorTrackerConfig>(
    (config) => ({
        name: "error-tracker",
        version: "1.0.0",

        onError: async (error, req, res, next) => {
            // Send error to tracking service
            await fetch("https://error-tracker.example.com/api/errors", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                    "X-API-Key": config.apiKey,
                },
                body: JSON.stringify({
                    error: {
                        message: error.message,
                        stack: error.stack,
                        name: error.name,
                    },
                    request: {
                        method: req.method,
                        url: req.url,
                        headers: req.headers,
                        ip: req.ip,
                    },
                    environment: config.environment,
                    timestamp: new Date().toISOString(),
                }),
            });

            // Continue error handling
            next();
        },
    })
);

Support and Resources


Copyright 2025 Nehonix Team
License: MIT