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

validation.mddocs/

Validation

Input validation system with support for Joi and other validation libraries for request payload, query parameters, and headers in @hapi/hapi.

Capabilities

Validation Configuration

Configure validation for different parts of the HTTP request including payload, query parameters, path parameters, and headers.

interface ValidationOptions {
    /** Validate request headers */
    headers?: object | boolean;
    /** Validate path parameters */
    params?: object | boolean;
    /** Validate query parameters */
    query?: object | boolean;
    /** Validate request payload */
    payload?: object | boolean;
    /** Validate cookie state */
    state?: object | boolean;
    /** Action to take when validation fails */
    failAction?: ValidationFailAction;
    /** Validation library options */
    options?: object;
    /** Custom error messages */
    errorFields?: object;
}

type ValidationFailAction = 'error' | 'log' | 'ignore' | Function;

Payload Validation

Validate request payload data with comprehensive schema definitions.

// Payload validation using Joi schema
const Joi = require('joi');

interface PayloadValidationSchema {
    /** Required fields validation */
    [key: string]: any;
}

Usage Examples:

const Joi = require('joi');

// Basic payload validation
server.route({
    method: 'POST',
    path: '/users',
    options: {
        validate: {
            payload: Joi.object({
                name: Joi.string().min(2).max(50).required(),
                email: Joi.string().email().required(),
                age: Joi.number().integer().min(18).max(120).optional(),
                address: Joi.object({
                    street: Joi.string().required(),
                    city: Joi.string().required(),
                    zipCode: Joi.string().pattern(/^\d{5}$/).required()
                }).optional()
            })
        }
    },
    handler: async (request, h) => {
        const user = await database.createUser(request.payload);
        return h.response(user).code(201);
    }
});

// Complex payload validation
server.route({
    method: 'POST',
    path: '/orders',
    options: {
        validate: {
            payload: Joi.object({
                customerId: Joi.string().uuid().required(),
                items: Joi.array().items(
                    Joi.object({
                        productId: Joi.string().uuid().required(),
                        quantity: Joi.number().integer().min(1).required(),
                        price: Joi.number().precision(2).positive().required()
                    })
                ).min(1).required(),
                shippingAddress: Joi.object({
                    name: Joi.string().required(),
                    street: Joi.string().required(),
                    city: Joi.string().required(),
                    state: Joi.string().length(2).required(),
                    zipCode: Joi.string().pattern(/^\d{5}(-\d{4})?$/).required()
                }).required(),
                paymentMethod: Joi.string().valid('credit_card', 'paypal', 'bank_transfer').required()
            })
        }
    },
    handler: async (request, h) => {
        const order = await orderService.createOrder(request.payload);
        return h.response(order).code(201);
    }
});

Query Parameter Validation

Validate URL query string parameters with type conversion and constraints.

Usage Examples:

// Query parameter validation
server.route({
    method: 'GET',
    path: '/users',
    options: {
        validate: {
            query: Joi.object({
                page: Joi.number().integer().min(1).default(1),
                limit: Joi.number().integer().min(1).max(100).default(10),
                sort: Joi.string().valid('name', 'email', 'created_at').default('created_at'),
                order: Joi.string().valid('asc', 'desc').default('desc'),
                search: Joi.string().min(2).max(50).optional(),
                active: Joi.boolean().default(true),
                role: Joi.array().items(Joi.string().valid('admin', 'user', 'moderator')).single().optional()
            })
        }
    },
    handler: async (request, h) => {
        const { page, limit, sort, order, search, active, role } = request.query;
        const users = await database.getUsers({
            page,
            limit,
            sort,
            order,
            search,
            active,
            role
        });
        return users;
    }
});

// Advanced query validation with custom logic
server.route({
    method: 'GET',
    path: '/analytics',
    options: {
        validate: {
            query: Joi.object({
                startDate: Joi.date().iso().required(),
                endDate: Joi.date().iso().min(Joi.ref('startDate')).required(),
                metrics: Joi.array().items(
                    Joi.string().valid('views', 'clicks', 'conversions', 'revenue')
                ).min(1).required(),
                groupBy: Joi.string().valid('day', 'week', 'month').default('day'),
                timezone: Joi.string().default('UTC')
            }).custom((value, helpers) => {
                const { startDate, endDate } = value;
                const daysDiff = (new Date(endDate) - new Date(startDate)) / (1000 * 60 * 60 * 24);
                
                if (daysDiff > 365) {
                    return helpers.error('custom.dateRange', { maxDays: 365 });
                }
                
                return value;
            })
        }
    },
    handler: async (request, h) => {
        const analytics = await analyticsService.getMetrics(request.query);
        return analytics;
    }
});

Path Parameter Validation

Validate URL path parameters with type checking and format validation.

Usage Examples:

// Path parameter validation
server.route({
    method: 'GET',
    path: '/users/{id}',
    options: {
        validate: {
            params: Joi.object({
                id: Joi.string().uuid().required()
            })
        }
    },
    handler: async (request, h) => {
        const user = await database.getUser(request.params.id);
        if (!user) {
            return h.response({ error: 'User not found' }).code(404);
        }
        return user;
    }
});

// Multiple path parameters
server.route({
    method: 'GET',
    path: '/users/{userId}/posts/{postId}',
    options: {
        validate: {
            params: Joi.object({
                userId: Joi.string().uuid().required(),
                postId: Joi.alternatives().try(
                    Joi.string().uuid(),
                    Joi.string().alphanum().min(5).max(20)
                ).required()
            })
        }
    },
    handler: async (request, h) => {
        const { userId, postId } = request.params;
        const post = await database.getUserPost(userId, postId);
        return post;
    }
});

// Path parameter with custom validation
server.route({
    method: 'GET',
    path: '/files/{path*}',
    options: {
        validate: {
            params: Joi.object({
                path: Joi.string().custom((value, helpers) => {
                    // Prevent directory traversal
                    if (value.includes('..') || value.includes('~')) {
                        return helpers.error('custom.invalidPath');
                    }
                    return value;
                }).required()
            })
        }
    },
    handler: async (request, h) => {
        const filePath = path.join('./uploads', request.params.path);
        return h.file(filePath);
    }
});

Header Validation

Validate HTTP headers including custom headers and standard headers.

Usage Examples:

// Header validation
server.route({
    method: 'POST',
    path: '/api/data',
    options: {
        validate: {
            headers: Joi.object({
                'content-type': Joi.string().valid('application/json').required(),
                'x-api-key': Joi.string().alphanum().length(32).required(),
                'user-agent': Joi.string().required(),
                'x-request-id': Joi.string().uuid().optional(),
                'x-client-version': Joi.string().pattern(/^\d+\.\d+\.\d+$/).optional()
            }).unknown(true) // Allow other headers
        }
    },
    handler: async (request, h) => {
        const apiKey = request.headers['x-api-key'];
        const requestId = request.headers['x-request-id'];
        
        // Process with validated headers
        const result = await processData(request.payload, { apiKey, requestId });
        return result;
    }
});

State (Cookie) Validation

Validate cookie values and state information.

Usage Examples:

// State validation
server.route({
    method: 'GET',
    path: '/dashboard',
    options: {
        validate: {
            state: Joi.object({
                session: Joi.object({
                    userId: Joi.string().uuid().required(),
                    role: Joi.string().valid('admin', 'user').required(),
                    expires: Joi.date().timestamp().required()
                }).required(),
                preferences: Joi.object({
                    theme: Joi.string().valid('light', 'dark').default('light'),
                    language: Joi.string().length(2).default('en')
                }).optional()
            })
        }
    },
    handler: async (request, h) => {
        const { session, preferences } = request.state;
        const dashboard = await getDashboard(session.userId, preferences);
        return dashboard;
    }
});

Custom Validation Functions

Create custom validation logic for complex scenarios.

Usage Examples:

// Custom validation function
const validateBusinessRules = (value, helpers) => {
    const { startDate, endDate, eventType } = value;
    
    // Business rule: Premium events require at least 30 days notice
    if (eventType === 'premium') {
        const daysNotice = (new Date(startDate) - new Date()) / (1000 * 60 * 60 * 24);
        if (daysNotice < 30) {
            return helpers.error('custom.premiumEventNotice', { minDays: 30 });
        }
    }
    
    // Business rule: Events cannot span more than 7 days
    const eventDuration = (new Date(endDate) - new Date(startDate)) / (1000 * 60 * 60 * 24);
    if (eventDuration > 7) {
        return helpers.error('custom.eventTooLong', { maxDays: 7 });
    }
    
    return value;
};

server.route({
    method: 'POST',
    path: '/events',
    options: {
        validate: {
            payload: Joi.object({
                title: Joi.string().min(5).max(100).required(),
                description: Joi.string().max(1000).optional(),
                startDate: Joi.date().iso().min('now').required(),
                endDate: Joi.date().iso().min(Joi.ref('startDate')).required(),
                eventType: Joi.string().valid('basic', 'premium').required(),
                attendeeLimit: Joi.number().integer().min(1).max(1000).required()
            }).custom(validateBusinessRules)
        }
    },
    handler: async (request, h) => {
        const event = await eventService.createEvent(request.payload);
        return h.response(event).code(201);
    }
});

Validation Error Handling

Handle validation errors with custom error messages and responses.

interface ValidationFailActionFunction {
    (request: Request, h: ResponseToolkit, error: Error): any;
}

Usage Examples:

// Custom validation error handler
const customValidationHandler = (request, h, error) => {
    const details = error.details.map(detail => ({
        field: detail.path.join('.'),
        message: detail.message,
        value: detail.context?.value
    }));
    
    return h.response({
        error: 'Validation Failed',
        statusCode: 400,
        details: details,
        timestamp: new Date().toISOString(),
        path: request.path
    }).code(400).takeover();
};

server.route({
    method: 'POST',
    path: '/users',
    options: {
        validate: {
            payload: Joi.object({
                name: Joi.string().min(2).required(),
                email: Joi.string().email().required(),
                password: Joi.string().min(8).required()
            }),
            failAction: customValidationHandler
        }
    },
    handler: async (request, h) => {
        const user = await userService.createUser(request.payload);
        return h.response(user).code(201);
    }
});

// Different fail actions per validation type
server.route({
    method: 'GET',
    path: '/search',
    options: {
        validate: {
            query: Joi.object({
                q: Joi.string().min(2).required(),
                category: Joi.string().valid('all', 'posts', 'users').default('all')
            }),
            failAction: 'log' // Just log query validation errors, don't fail request
        }
    },
    handler: async (request, h) => {
        // Will use default values for invalid query params
        const results = await searchService.search(request.query);
        return results;
    }
});

Server-Level Validation Configuration

Set global validation settings and custom validators.

/**
 * Set validation engine for the server
 * @param validator - Validation library (Joi, etc.)
 */
validator(validator: object): void;

/**
 * Set validation rules processor
 * @param processor - Rules processing function
 * @param options - Processor options
 */
rules(processor: Function, options?: object): void;

Usage Examples:

const Joi = require('joi');

// Set global validation engine
server.validator(Joi);

// Custom validation rules
server.rules((schema, options) => {
    // Custom preprocessing of validation schemas
    return schema.options({
        allowUnknown: options.allowUnknown !== false,
        stripUnknown: true,
        abortEarly: false
    });
});

// Global validation defaults in server options
const server = Hapi.server({
    port: 3000,
    routes: {
        validate: {
            options: {
                abortEarly: false,
                stripUnknown: true
            },
            failAction: (request, h, error) => {
                console.log('Validation error:', error.details);
                throw error;
            }
        }
    }
});

Types

interface ValidationError extends Error {
    /** Validation error details */
    details: ValidationErrorDetail[];
    /** Whether error is a validation error */
    isJoi: boolean;
}

interface ValidationErrorDetail {
    /** Error message */
    message: string;
    /** Field path that failed validation */
    path: (string | number)[];
    /** Validation error type */
    type: string;
    /** Validation context */
    context?: {
        value?: any;
        key?: string;
        label?: string;
        [key: string]: any;
    };
}

interface ValidateRouteOptions {
    /** Header validation schema */
    headers?: object | boolean;
    /** Parameter validation schema */
    params?: object | boolean;
    /** Query validation schema */
    query?: object | boolean;
    /** Payload validation schema */
    payload?: object | boolean;
    /** State validation schema */
    state?: object | boolean;
    /** Validation failure action */
    failAction?: ValidationFailAction;
    /** Validation options */
    options?: object;
}

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