or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

authentication.mdcaching.mdextensions.mdindex.mdplugin-system.mdrequest-processing.mdresponse-management.mdrouting.mdserver-management.mdvalidation.md
tile.json

extensions.mddocs/

Extensions

Lifecycle hooks for intercepting and modifying request processing at various stages of the pipeline in @hapi/hapi.

Capabilities

Extension Points

Extensions allow you to hook into the request lifecycle at specific points to modify processing behavior.

/**
 * Register request lifecycle extensions
 * @param events - Extension point(s) to hook into
 * @param method - Extension method or array of methods
 * @param options - Extension options
 */
ext(events: ExtensionEvent | ExtensionEvent[] | ExtensionPoint[], method?: ExtensionMethod, options?: ExtensionOptions): void;

type ExtensionEvent = 
    | 'onPreStart'      // Before server starts
    | 'onPostStart'     // After server starts
    | 'onPreStop'       // Before server stops
    | 'onPostStop'      // After server stops
    | 'onRequest'       // On incoming request
    | 'onPreAuth'       // Before authentication
    | 'onCredentials'   // After credential lookup
    | 'onPostAuth'      // After authentication
    | 'onPreHandler'    // Before route handler
    | 'onPostHandler'   // After route handler
    | 'onPreResponse'   // Before response processing
    | 'onPostResponse'; // After response sent

interface ExtensionPoint {
    /** Extension event */
    type: ExtensionEvent;
    /** Extension method */
    method: ExtensionMethod;
    /** Extension options */
    options?: ExtensionOptions;
}

type ExtensionMethod = (request: Request, h: ResponseToolkit, err?: Error) => any;

interface ExtensionOptions {
    /** Extension execution order */
    before?: string | string[];
    /** Extension execution order */
    after?: string | string[];
    /** Context binding */
    bind?: object;
    /** Sandbox isolation */
    sandbox?: 'server' | 'plugin';
}

Server Lifecycle Extensions

Extensions that run during server startup and shutdown.

// Server lifecycle extension events:
// 'onPreStart'  - Before server starts listening
// 'onPostStart' - After server starts listening
// 'onPreStop'   - Before server stops
// 'onPostStop'  - After server stops

Usage Examples:

// Database connection on server start
server.ext('onPreStart', async (server) => {
    console.log('Connecting to database...');
    await database.connect();
    console.log('Database connected');
});

// Cleanup on server stop
server.ext('onPreStop', async (server) => {
    console.log('Closing database connection...');
    await database.disconnect();
    console.log('Database disconnected');
});

// Log server status
server.ext('onPostStart', (server) => {
    console.log(`Server started at: ${server.info.uri}`);
});

server.ext('onPostStop', (server) => {
    console.log('Server stopped');
});

Request Lifecycle Extensions

Extensions that run during request processing with access to request and response toolkit.

// Request lifecycle extension events:
// 'onRequest'     - First request processing step
// 'onPreAuth'     - Before authentication
// 'onCredentials' - After credential lookup
// 'onPostAuth'    - After authentication
// 'onPreHandler'  - Before route handler execution
// 'onPostHandler' - After route handler execution
// 'onPreResponse' - Before response transmission
// 'onPostResponse'- After response sent (cleanup)

Usage Examples:

// Request logging and timing
server.ext('onRequest', (request, h) => {
    request.app.startTime = Date.now();
    console.log(`${request.method.toUpperCase()} ${request.path} - Started`);
    return h.continue;
});

// Add request ID
server.ext('onRequest', (request, h) => {
    request.app.requestId = require('uuid').v4();
    return h.continue;
});

// Security headers
server.ext('onPreResponse', (request, h) => {
    const response = request.response;
    if (response.isBoom) {
        return h.continue;
    }
    
    response.header('X-Request-ID', request.app.requestId);
    response.header('X-Content-Type-Options', 'nosniff');
    response.header('X-Frame-Options', 'DENY');
    response.header('X-XSS-Protection', '1; mode=block');
    
    return h.continue;
});

// Response timing
server.ext('onPreResponse', (request, h) => {
    const duration = Date.now() - request.app.startTime;
    console.log(`${request.method.toUpperCase()} ${request.path} - ${duration}ms`);
    return h.continue;
});

Authentication Extensions

Extensions specifically for authentication processing.

Usage Examples:

// Pre-authentication processing
server.ext('onPreAuth', (request, h) => {
    // Log authentication attempts
    if (request.headers.authorization) {
        console.log(`Auth attempt for ${request.path}`);
    }
    return h.continue;
});

// Post-authentication processing
server.ext('onPostAuth', (request, h) => {
    if (request.auth.isAuthenticated) {
        // Log successful authentication
        console.log(`User ${request.auth.credentials.user} authenticated`);
        
        // Add user info to request
        request.app.user = request.auth.credentials;
    }
    return h.continue;
});

// Credentials processing
server.ext('onCredentials', (request, h) => {
    // Modify or enhance credentials
    if (request.auth.credentials) {
        request.auth.credentials.loginTime = Date.now();
    }
    return h.continue;
});

Handler Extensions

Extensions around route handler execution.

Usage Examples:

// Pre-handler validation
server.ext('onPreHandler', (request, h) => {
    // Additional validation logic
    if (request.path.startsWith('/admin/') && !request.auth.credentials?.scope?.includes('admin')) {
        return h.response({ error: 'Insufficient privileges' }).code(403).takeover();
    }
    return h.continue;
});

// Post-handler processing
server.ext('onPostHandler', (request, h) => {
    const response = request.response;
    
    // Modify response based on user preferences
    if (request.auth.credentials?.preferences?.format === 'xml') {
        // Convert JSON to XML (example)
        response.type('application/xml');
    }
    
    return h.continue;
});

Error Handling Extensions

Extensions for handling errors during request processing.

Usage Examples:

// Global error handling
server.ext('onPreResponse', (request, h) => {
    const response = request.response;
    
    if (response.isBoom) {
        // Log error
        console.error(`Error ${response.output.statusCode}: ${response.message}`);
        
        // Custom error response format
        const customError = {
            error: {
                statusCode: response.output.statusCode,
                message: response.message,
                requestId: request.app.requestId,
                timestamp: new Date().toISOString()
            }
        };
        
        return h.response(customError).code(response.output.statusCode);
    }
    
    return h.continue;
});

Extension Ordering

Control the order of extension execution using before/after options.

Usage Examples:

// Extension that runs first
server.ext('onRequest', (request, h) => {
    request.app.step1 = 'completed';
    return h.continue;
}, {
    before: '*' // Run before all other extensions
});

// Extension that runs after another
server.ext('onRequest', (request, h) => {
    request.app.step2 = 'completed';
    return h.continue;
}, {
    after: 'step1-extension'
});

// Multiple ordering constraints
server.ext('onPreResponse', (request, h) => {
    // Add final headers
    return h.continue;
}, {
    after: ['security-headers', 'cors-headers'],
    before: 'response-compression'
});

Route-Level Extensions

Extensions can also be defined at the route level.

interface RouteExtensions {
    /** Route-level extensions */
    [key: string]: ExtensionMethod | ExtensionMethod[];
}

Usage Examples:

server.route({
    method: 'GET',
    path: '/special',
    options: {
        ext: {
            onPreHandler: (request, h) => {
                // Route-specific pre-handler logic
                request.app.specialRoute = true;
                return h.continue;
            },
            onPostHandler: [
                (request, h) => {
                    // First post-handler
                    return h.continue;
                },
                (request, h) => {
                    // Second post-handler
                    return h.continue;
                }
            ]
        }
    },
    handler: (request, h) => {
        return { special: request.app.specialRoute };
    }
});

Extension Return Values

Extensions can return different values to control request processing flow.

// Extension return values:
// h.continue         - Continue normal processing
// h.close            - Close connection without response
// h.abandon          - Abandon request processing  
// Response object    - Override normal response
// Error/Boom         - Return error response
// Promise            - Async extension (await result)

Usage Examples:

// Conditional processing
server.ext('onPreHandler', (request, h) => {
    if (request.path === '/maintenance') {
        return h.response({ message: 'Service under maintenance' })
                .code(503)
                .takeover();
    }
    return h.continue;
});

// Async extension
server.ext('onPreAuth', async (request, h) => {
    // Rate limiting check
    const isAllowed = await rateLimit.check(request.info.remoteAddress);
    if (!isAllowed) {
        return h.response({ error: 'Rate limit exceeded' })
                .code(429)
                .takeover();
    }
    return h.continue;
});

// Connection management
server.ext('onRequest', (request, h) => {
    if (server.load.heapUsed > MAX_HEAP_SIZE) {
        return h.close; // Close connection immediately
    }
    return h.continue;
});

Types

interface Request {
    /** Application state modified by extensions */
    app: object;
    /** Plugin state modified by extensions */
    plugins: object;
    /** Request logs including extension logs */
    logs: LogEntry[];
}

interface ResponseToolkit {
    /** Continue normal processing */
    continue: symbol;
    /** Close connection */
    close: symbol;
    /** Abandon request processing */
    abandon: symbol;
}