CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-hapi--hapi

HTTP Server framework for Node.js with built-in authentication, validation, caching, logging, and 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

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

docs

authentication.md

caching.md

extensions.md

index.md

plugin-system.md

request-processing.md

response-management.md

routing.md

server-management.md

validation.md

tile.json