UNPKG

@catbee/utils

Version:

A modular, production-grade utility toolkit for Node.js and TypeScript, designed for robust, scalable applications (including Express-based services). All utilities are tree-shakable and can be imported independently.

890 lines (885 loc) 29.4 kB
/* * The MIT License * * Copyright (c) 2026 Catbee Technologies. https://catbee.in/license * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ import * as prom_client from 'prom-client'; import express, { Express, Router } from 'express'; import http from 'node:http'; import https from 'node:https'; import { CatbeeServerConfig, CatbeeServerHooks } from '@catbee/utils/types'; /** * Map of critical dependencies to their error messages. */ declare const DependencyErrors: { express: string; helmet: string; cors: string; compression: string; 'express-rate-limit': string; 'cookie-parser': string; '@scalar/express-api-reference': string; 'prom-client': string; }; /** * Production-ready Express server with enterprise features. * * Core Features: * - Security: Helmet, CORS, rate limiting, timeouts * - Monitoring: Request logs, metrics, health checks * - Performance: Compression, caching, static files * - Reliability: Graceful shutdown, error handling * - Developer UX: OpenAPI docs, debugging tools * - Extensibility: Hooks, middleware, custom routes * * Designed for microservices and production workloads. * Includes K8s readiness probes and zero-downtime support. */ declare class ExpressServer { /** Prometheus client registry for metrics collection */ private readonly register; /** HTTP server instance (null when not running) */ protected server: http.Server | https.Server | null; /** Merged configuration with defaults applied */ protected config: CatbeeServerConfig; /** User-defined lifecycle hooks */ protected hooks: CatbeeServerHooks; /** Global API prefix (from config) */ protected globalPrefix: string; /** Internal fallback router */ private readonly rootRouter; /** User-supplied router */ private externalRouter?; /** Internal Express app instance */ private readonly app; /** Set of active WebSocket connections */ private readonly connections; /** Flag indicating if the server is shutting down */ private isShuttingDown; /** * Collection of registered health check functions. * These are executed when the health check endpoint is accessed. */ private readonly healthChecks; /** Prometheus metrics for monitoring */ private readonly requestCounter?; private readonly routeTimings?; private readonly requestSizes?; private readonly clientIPs?; /** Promise that resolves when initialization (middleware + routes) is complete */ private readonly initPromise; /** * Initializes server with intelligent defaults and security best practices. * All settings can be customized via config and hooks. * * Default Security: * - Secure headers (Helmet) * - Rate limiting * - Request timeouts * - Body size limits * - CORS protection * * Default Monitoring: * - Request/Response logging * - Prometheus metrics * - Health checks * - Request tracing */ constructor(config: Partial<CatbeeServerConfig>, hooks?: CatbeeServerHooks); /** * Execute a lifecycle hook safely with comprehensive error handling. * Prevents hook failures from crashing the server while logging issues. * * @param hook Name of the lifecycle hook to execute * @param args Arguments to pass to the hook function */ private runHook; /** * Initialize the Express server with middleware and routes. */ private initialize; /** * Configure and register all middlewares in the optimal order. * * Middleware Order (CRITICAL - don't change without understanding implications): * 1. Basic server configuration (trust proxy, x-powered-by) * 2. Request ID generation (for tracing) * 3. Request context setup (for logging correlation) * 4. Timeout protection (prevents hanging requests) * 5. Response time tracking (for performance monitoring) * 6. Request logging (after ID/context setup) * 7. Custom request hooks * 8. Security middleware (rate limiting, CORS, Helmet) * 9. Response compression * 10. Static file serving * 11. Request parsing (body parsing, cookies) * 12. API documentation (OpenAPI) * 13. Global headers * 14. Custom response hooks */ protected setupMiddleware(): Promise<void>; /** * Set up basic middleware (trust proxy, request ID, context). */ private setupBasicMiddleware; /** * Set up security middleware (Helmet, CORS). */ private setupSecurityMiddleware; /** * Set up global headers middleware. */ private setupGlobalHeaders; /** * Set up request timeout middleware. */ private setupTimeoutMiddleware; /** * Set up response time tracking middleware. */ private setupResponseTimeMiddleware; /** * Set up rate limiting middleware. */ private setupRateLimitingMiddleware; /** * Set up request logging middleware. */ private setupRequestLoggingMiddleware; /** * Set up response compression middleware. */ private setupCompressionMiddleware; /** * Set up static file serving middleware. */ private setupStaticFilesMiddleware; /** * Set up body parsing middleware. */ private setupBodyParsingMiddleware; /** * Set up cookie parsing middleware. */ private setupCookieParsingMiddleware; /** * Set up OpenAPI documentation middleware. */ private setupOpenApiMiddleware; /** * Set up metrics tracking middleware. */ private setupMetricsMiddleware; /** * Configure server routes and error handling. * Sets up in following order: * * 1. Built-in routes (health, metrics) * 2. Application routes * 3. 404 handler * 4. Error handler */ protected setupRoutes(): Promise<void>; /** * Execute health check and return response. */ private handleHealthCheckRequest; /** * Execute all registered health checks and return results. */ private executeHealthChecks; /** * Register a new health check function for monitoring service dependencies. * * Health checks are executed when the health endpoint is accessed and * help determine if the service is ready to handle requests. * * Examples: * - Database connectivity * - External service availability * - File system access * - Memory/CPU usage checks * * @param name Unique identifier for the check (used in detailed responses) * @param check Function returning boolean or Promise<boolean> indicating health * @returns This instance for method chaining */ registerHealthCheck(name: string, check: () => Promise<boolean> | boolean): this; /** * Run registered health checks and return whether the service is ready. * Useful for readiness probes in deployment tooling. * * @returns Promise resolving to `true` when all checks pass, otherwise `false`. */ ready(): Promise<boolean>; /** * Get the underlying Express application instance. * Use this for advanced Express features not exposed by this wrapper. * * @returns The raw Express app instance */ getApp(): Express; /** * Get the active HTTP/HTTPS server instance. * Returns null if the server is not currently running. * * @returns The HTTP/HTTPS server instance or null */ getServer(): http.Server | https.Server | null; /** * Start the HTTP server and begin listening for requests. * * This method: * - Executes beforeStart hooks * - Binds to the configured host/port * - Sets up error handling for startup failures * - Executes afterStart hooks on success * - Logs startup information * * @returns Promise resolving to the running HTTP server instance * @throws Error if server fails to start or port is already in use */ start(): Promise<http.Server | https.Server>; /** * Create HTTP or HTTPS server instance. */ private createServerInstance; /** * Set up connection tracking for graceful shutdown. */ private setupConnectionTracking; /** * Set up error handling for server startup. */ private setupServerErrorHandling; /** * Log server startup information. */ private logServerStartInfo; /** * Stop the HTTP server gracefully. * * This method: * - Executes beforeStop hooks * - Stops accepting new connections * - Waits for existing connections to finish * - Closes the server * - Executes afterStop hooks * - Logs shutdown information * * Graceful shutdown ensures: * - No requests are dropped * - Resources are properly cleaned up * - Monitoring systems are notified */ stop(force?: boolean): Promise<void>; /** * Perform graceful server shutdown with timeout. */ private gracefulShutdown; /** * Enable graceful shutdown on OS signals for production deployment. * * This is essential for: * - Container orchestration (Docker, Kubernetes) * - Process managers (PM2, systemd) * - Load balancer health checks * - Zero-downtime deployments * * @param signals Array of process signals to listen for (default: SIGINT, SIGTERM) */ enableGracefulShutdown(signals?: NodeJS.Signals[]): this; /** * Set an externally created base router. * This will override the internal rootRouter. */ setBaseRouter(router: Router): this; /** * Create and register a new router (only used if not injecting one externally - use `setBaseRouter` instead). */ createRouter(prefix?: string): Router; /** * Register a new route handler with support for multiple HTTP methods. * The route is automatically registered under the globalPrefix if set. * * @param methods Array of HTTP methods (get, post, put, delete, etc.) * @param path Route path with Express path patterns support * @param handlers One or more Express request handlers (middleware + final handler) * @returns This instance for method chaining */ registerRoute(methods: Array<keyof Pick<Express, 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options' | 'head'>>, path: string, ...handlers: Array<express.RequestHandler>): this; /** * Register custom middleware with optional path restriction. * * Use this for: * - Adding authentication to specific routes * - Custom logging or validation * - Request transformation * - Third-party middleware integration * * @param path Optional path prefix or middleware function if no path * @param middleware Middleware handler (required if path is provided) * @returns This instance for method chaining */ registerMiddleware(path: string | express.RequestHandler, middleware?: express.RequestHandler): this; /** * Register one or more middleware functions to be applied globally. * This is a simpler alternative to registerMiddleware when you just want * to add middleware without path restrictions. * * @param middlewares One or more Express middleware functions * @returns This instance for method chaining */ useMiddleware(...middlewares: express.RequestHandler[]): this; /** * Get Prometheus registry (to add custom counters/histograms) * * @return {*} {client.Registry} */ getMetricsRegistry(): typeof prom_client.Registry; /** * Get server configuration * * @return {*} {CatbeeServerConfig} */ getConfig(): CatbeeServerConfig; /** * Wait until server initialization (middleware + routes) has completed. * Useful for integration tests that inspect app before starting. */ waitUntilReady(): Promise<void>; private normalizePath; private normalizeRouteForMetrics; /** * Destroy all active connections (gracefully if possible). * If a connection does not close cleanly, it will be force-destroyed. */ private destroyConnections; private validateHttpsFiles; private hasBuildMarker; private throwDependancyError; } /** * Builder class for creating and configuring an Express server configuration. * * This class provides a fluent interface to configure all aspects of the Express server * including security settings, middleware, routing, and more. * * @example * ```typescript * const serverConfig = new ServerConfigBuilder() * .withPort(3000) * .withHost('localhost') * .enableCors() * .enableHelmet() * .build(); * ``` */ declare class ServerConfigBuilder { private config; /** * Validates that a port number is valid and usable. * * @private * @param port - The port number to validate * @throws {Error} If port is not an integer or is outside the valid range (1-65535) */ private validatePort; /** * Sets the port the server will listen on. * * @param port - The port number (1-65535) * @returns The builder instance for chaining * @throws {Error} If port is invalid * @default 3000 (can be overridden via PORT env variable) * * @example * ```typescript * builder.withPort(3000) * ``` */ withPort(port: number): this; /** * Sets the hostname the server will bind to. * * @param host - The hostname (e.g., 'localhost', '0.0.0.0', '127.0.0.1') * @returns The builder instance for chaining * @default '0.0.0.0' (can be overridden via HOST env variable) * * @example * ```typescript * builder.withHost('0.0.0.0') // Listen on all interfaces * ``` */ withHost(host: string): this; /** * Configures Cross-Origin Resource Sharing (CORS) for the server. * * @param opts - CORS options object or boolean (true to enable with defaults, false to disable) * @returns The builder instance for chaining * @default false (CORS is disabled by default) * * @example * ```typescript * // Enable CORS with default options * builder.withCors(true) * * // Configure CORS with specific options * builder.withCors({ * origin: ['https://example.com'], * methods: ['GET', 'POST'] * }) * ``` */ withCors(opts: CatbeeServerConfig['cors']): this; /** * Enables CORS with default settings * * @returns The builder instance for chaining */ enableCors(): this; /** * Disables CORS * * @returns The builder instance for chaining */ disableCors(): this; /** * Configures the Helmet middleware for setting HTTP security headers. * * @param opts - Helmet options object or boolean (true to enable with defaults, false to disable) * @returns The builder instance for chaining * @default false (Helmet is disabled by default) * * @example * ```typescript * // Enable Helmet with default settings * builder.withHelmet(true) * * // Configure Helmet with specific options * builder.withHelmet({ * contentSecurityPolicy: false, * xssFilter: true * }) * ``` */ withHelmet(opts: CatbeeServerConfig['helmet']): this; /** * Enables Helmet with default settings * * @returns The builder instance for chaining */ enableHelmet(): this; /** * Disables Helmet * * @returns The builder instance for chaining */ disableHelmet(): this; /** * Configures response compression middleware. * * @param opts - Compression options object or boolean (true to enable with defaults, false to disable) * @returns The builder instance for chaining * @default false (Compression is disabled by default) * * @example * ```typescript * // Enable compression with default settings * builder.withCompression(true) * * // Configure compression with specific options * builder.withCompression({ * level: 6, * threshold: 1024 * }) * ``` */ withCompression(opts: CatbeeServerConfig['compression']): this; /** * Enables compression with default settings * * @returns The builder instance for chaining */ enableCompression(): this; /** * Disables compression * * @returns The builder instance for chaining */ disableCompression(): this; /** * Configures rate limiting to protect against brute-force attacks. * * @param opts - Rate limit configuration options * @returns The builder instance for chaining * @default - { enable: false, windowMs: 15 * 60 * 1000, max: 100, message: 'Too many requests', standardHeaders: true, legacyHeaders: false } * * @example * ```typescript * builder.withRateLimit({ * enable: true, * windowMs: 15 * 60 * 1000, // 15 minutes * max: 100 // limit each IP to 100 requests per windowMs * }) * ``` */ withRateLimit(opts: Partial<CatbeeServerConfig['rateLimit']>): this; /** * Enables rate limiting with default or custom settings * * @param opts - Optional rate limit configuration (max requests, window, etc.) * @returns The builder instance for chaining */ enableRateLimit(opts?: Omit<Partial<NonNullable<CatbeeServerConfig['rateLimit']>>, 'enable'>): this; /** * Disables rate limiting * * @returns The builder instance for chaining */ disableRateLimit(): this; /** * Configures HTTP request logging middleware. * * @param opts - Request logging configuration options * @returns The builder instance for chaining * @default - { enable: true in dev/false in prod, ignorePaths: ['/healthz', '/favicon.ico', '/metrics', '/docs', '/.well-known'], skipNotFoundRoutes: false } * * @example * ```typescript * builder.withRequestLogging({ * enable: true, * ignorePaths: ['/health', '/metrics'], * skipNotFoundRoutes: true * }) * ``` */ withRequestLogging(opts: Partial<NonNullable<CatbeeServerConfig['requestLogging']>>): this; /** * Enables request logging with default or custom settings * * @param opts - Optional request logging configuration * @returns The builder instance for chaining */ enableRequestLogging(opts?: Omit<Partial<NonNullable<CatbeeServerConfig['requestLogging']>>, 'enable'>): this; /** * Disables request logging * * @returns The builder instance for chaining */ disableRequestLogging(): this; /** * Configures server metrics collection and endpoints. * * @param opts - Metrics configuration options * @returns The builder instance for chaining * @default - { enable: false, path: '/metrics', withGlobalPrefix: false } * * @example * ```typescript * builder.withMetrics({ * enable: true, * path: '/metrics' * }) * ``` */ withMetrics(opts: Partial<NonNullable<CatbeeServerConfig['metrics']>>): this; /** * Enables Prometheus metrics collection and endpoint * * @param opts - Optional metrics configuration * @returns The builder instance for chaining */ enableMetrics(opts?: Omit<Partial<NonNullable<CatbeeServerConfig['metrics']>>, 'enable'>): this; /** * Disables Prometheus metrics * * @returns The builder instance for chaining */ disableMetrics(): this; /** * Configures server health check endpoint. * * @param opts - Health check configuration options * @returns The builder instance for chaining * @default - { path: '/healthz', detailed: true, withGlobalPrefix: false } * * @example * ```typescript * builder.withHealthCheck({ * path: '/health', * detailed: true * }) * ``` */ withHealthCheck(opts: Partial<NonNullable<CatbeeServerConfig['healthCheck']>>): this; /** * Configures OpenAPI/Swagger documentation for the API. * * @param opts - OpenAPI configuration options * @returns The builder instance for chaining * @default - { enable: false, mountPath: '/docs', verbose: false, withGlobalPrefix: false } * * @example * ```typescript * builder.withOpenApi({ * enable: true, * path: '/api-docs', * filePath: './openapi.yaml' * }) * ``` */ withOpenApi(opts: Partial<NonNullable<CatbeeServerConfig['openApi']>>): this; /** * Enables OpenAPI documentation with required file path * * @param filePath - Path to OpenAPI specification file (required) * @param opts - Optional OpenAPI configuration * @returns The builder instance for chaining */ enableOpenApi(filePath: string, opts?: Omit<Partial<NonNullable<CatbeeServerConfig['openApi']>>, 'enable' | 'filePath'>): this; /** * Disables OpenAPI documentation * * @returns The builder instance for chaining */ disableOpenApi(): this; /** * Configures the server as a microservice with versioning. * * @param opts - Microservice configuration options including app name and service version * @returns The builder instance for chaining * @default - { isMicroservice: false, appName: 'express_app' } * * @example * ```typescript * builder.withMicroService({ * appName: 'user-service', * serviceVersion: { * enable: true, * version: '1.2.3' * } * }) * ``` */ withMicroService(opts: { appName: NonNullable<CatbeeServerConfig['appName']>; serviceVersion: Partial<NonNullable<CatbeeServerConfig['serviceVersion']>>; }): this; /** * Configures the trust proxy settings to determine if X-Forwarded-* headers should be trusted. * * @param opts - Trust proxy configuration options * @returns The builder instance for chaining * @default false * * @example * ```typescript * // Trust proxy headers (useful when behind a load balancer) * builder.withTrustProxy(true) * ``` */ withTrustProxy(opts: NonNullable<CatbeeServerConfig['trustProxy']>): this; /** * Configures the request ID middleware for tracing requests across services. * * @param opts - Request ID configuration options * @returns The builder instance for chaining * @default - { headerName: 'x-request-id', exposeHeader: true } * * @example * ```typescript * builder.withRequestId({ * headerName: 'X-Request-Id', * generator: () => crypto.randomUUID() * }) * ``` */ withRequestId(opts: Partial<NonNullable<CatbeeServerConfig['requestId']>>): this; /** * Configures the response time middleware for measuring request processing times. * * @param opts - Response time configuration options * @returns The builder instance for chaining * @default - { enable: false, addHeader: true, logOnComplete: false } * * @example * ```typescript * builder.withResponseTime({ * enable: true, * addHeader: true, * logOnComplete: true * }) * ``` */ withResponseTime(opts: Partial<NonNullable<CatbeeServerConfig['responseTime']>>): this; /** * Enables response time tracking with default or custom settings * * @param opts - Optional response time configuration * @returns The builder instance for chaining */ enableResponseTime(opts?: Omit<Partial<NonNullable<CatbeeServerConfig['responseTime']>>, 'enable'>): this; /** * Disables response time tracking * * @returns The builder instance for chaining */ disableResponseTime(): this; /** * Configures the body parser middleware options for parsing request bodies. * * @param opts - Body parser configuration options * @returns The builder instance for chaining * @default - { json: { limit: '1mb' }, urlencoded: { extended: true, limit: '1mb' } } * * @example * ```typescript * builder.withBodyParser({ * json: { * limit: '1mb' * }, * urlencoded: { * extended: true, * limit: '1mb' * } * }) * ``` */ withBodyParser(opts: NonNullable<CatbeeServerConfig['bodyParser']>): this; /** * Configures cookie parsing middleware. * * @param opts - Cookie parser options or boolean (true to enable with defaults, false to disable) * @returns The builder instance for chaining * @default false * * @example * ```typescript * // Enable cookie parsing with default options * builder.withCookies(true) * * // Enable cookie parsing with specific options * builder.withCookies({ * secret: 'your-secret-key', * secure: true * }) * ``` */ withCookies(opts: CatbeeServerConfig['cookieParser']): this; /** * Adds a static folder to serve files from. * * @param folder - Static folder configuration * @returns The builder instance for chaining * * @example * ```typescript * builder.withStaticFolder({ * path: '/assets', * directory: './public', * options: { maxAge: '1d' } * }) * ``` */ withStaticFolder(folder: NonNullable<CatbeeServerConfig['staticFolders']>[number]): this; /** * Sets global headers to be included in all responses. * * @param headers - Object containing header name/value pairs or functions that return values * @returns The builder instance for chaining * @default - {} * * @example * ```typescript * builder.withGlobalHeaders({ * 'X-Powered-By': 'Catbee', * 'Server-Time': () => new Date().toISOString() * }) * ``` */ withGlobalHeaders(headers: NonNullable<CatbeeServerConfig['globalHeaders']>): this; /** * Sets a global prefix for all routes. * * @param prefix - The prefix to prepend to all routes (e.g., '/api/v1') * @returns The builder instance for chaining * @default '/' * * @example * ```typescript * builder.withGlobalPrefix('/api/v1') * ``` */ withGlobalPrefix(prefix: string): this; /** * Applies custom configuration overrides directly. * * @param overrides - Custom configuration options to merge * @returns The builder instance for chaining * * @example * ```typescript * builder.withCustom({ * port: 8080, * customMiddleware: myMiddlewareFunction * }) * ``` */ withCustom(overrides: Partial<CatbeeServerConfig>): this; /** * Configures HTTPS server options. * * @param opts - HTTPS configuration (key, cert, ca, passphrase, etc.) * @returns The builder instance for chaining * * @example * ```typescript * builder.withHttps({ * key: './localhost-key.pem', * cert: './localhost-cert.pem' * }) * ``` */ withHttps(opts: NonNullable<CatbeeServerConfig['https']>): this; /** * Builds and returns the final server configuration. * * This method merges the user-specified configuration with default values, * ensures all sections with 'enable' flags are properly structured, and * produces the final configuration to be used by the server. * * @returns The complete ServerConfig object * * @example * ```typescript * const config = new ServerConfigBuilder() * .withPort(3000) * .withHost('localhost') * .withCors(true) * .build(); * ``` */ build(): Readonly<CatbeeServerConfig>; private mergeConfig; private setEnabled; } export { DependencyErrors, ExpressServer, ServerConfigBuilder };