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.

163 lines 7.14 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.requestId = requestId; exports.responseTime = responseTime; exports.timeout = timeout; exports.errorHandler = errorHandler; const crypto_1 = require("crypto"); const config_1 = require("../config"); const id_utils_1 = require("./id.utils"); const http_status_codes_1 = require("./http-status-codes"); const response_utils_1 = require("./response.utils"); const env_utils_1 = require("./env.utils"); const logger_utils_1 = require("./logger.utils"); /** * Attaches a unique request ID to each request. * Useful for request tracing and correlation between logs. * * @param {object} [options] - Configuration options * @param {string} [options.headerName='X-Request-ID'] - Header name for request ID * @param {boolean} [options.exposeHeader=true] - Whether to expose the header in response * @returns {Middleware} Express-compatible middleware */ function requestId(options) { const headerName = (options === null || options === void 0 ? void 0 : options.headerName) || "X-Request-ID"; const exposeHeader = (options === null || options === void 0 ? void 0 : options.exposeHeader) !== false; return (req, res, next) => { // Use existing request ID from header or generate a new one const existingId = req.headers[headerName.toLowerCase()]; const id = existingId || (0, crypto_1.randomUUID)(); // Attach ID to request object req.id = id; // Add ID to response headers if (exposeHeader) { res.setHeader(headerName, id); } next(); }; } /** * Measures request processing time and logs or adds it to response headers. * * @param {object} [options] - Configuration options * @param {boolean} [options.addHeader=true] - Whether to add X-Response-Time header * @param {boolean} [options.logOnComplete=false] - Whether to log timing info * @returns {Middleware} Express-compatible middleware */ function responseTime(options) { const addHeader = (options === null || options === void 0 ? void 0 : options.addHeader) !== false; const logOnComplete = (options === null || options === void 0 ? void 0 : options.logOnComplete) === true; return (req, res, next) => { const start = process.hrtime(); // Function to calculate elapsed time const calculateDuration = () => { const diff = process.hrtime(start); return diff[0] * 1e3 + diff[1] * 1e-6; // Convert to ms }; // Handle response completion res.on("finish", () => { const duration = calculateDuration(); if (addHeader) { res.setHeader("X-Response-Time", `${duration.toFixed(2)}ms`); } if (logOnComplete) { const { method, url } = req; (0, logger_utils_1.getLogger)().info(`${method} ${url} - ${duration.toFixed(2)}ms`); } }); next(); }; } /** * Request timeout middleware. * Aborts requests that take too long to process. * * @param {number} [timeoutMs=30000] - Timeout in milliseconds * @returns {Middleware} Express-compatible middleware */ function timeout(timeoutMs = config_1.Config.Http.timeout) { return (req, res, next) => { // Set timeout for the request const timer = setTimeout(() => { res.status(408).json({ error: true, message: "Request timeout", status: 408, timestamp: new Date().toISOString(), }); }, timeoutMs); // Clear timeout when response is sent res.on("finish", () => { clearTimeout(timer); }); next(); }; } /** * Creates a standardized error response object for API errors. * * @param {string} message - Error message * @param {Request} req - Express request object * @param {any} error - Original error object * @param {object} [options] - Additional options * @param {boolean} [options.includeDetails=false] - Whether to include error details in non-production * @returns {object} Formatted error response */ const createErrorResponse = (message, req, error, options) => { const isDev = env_utils_1.Env.isDev(); const includeDetails = (options === null || options === void 0 ? void 0 : options.includeDetails) && isDev; const response = { error: true, message, timestamp: new Date().toISOString(), requestId: (error === null || error === void 0 ? void 0 : error.requestId) || req.id || (0, id_utils_1.uuid)(), path: req.originalUrl || req.url, }; // Include error code if present if (error === null || error === void 0 ? void 0 : error.code) { response.code = error.code; } // Include stack trace in development mode if requested if (includeDetails && (error === null || error === void 0 ? void 0 : error.stack)) { response.stack = error.stack.split("\n").map((line) => line.trim()); } return response; }; /** * Global error handling middleware with enhanced features. * * @param {object} [options] - Error handler options * @param {boolean} [options.logErrors=true] - Whether to log errors * @param {boolean} [options.includeDetails=false] - Whether to include error details in non-production * @param {Function} [options.logger=getLogger().error] - Custom logging function * @returns {(err: any, req: Request, res: Response, next: NextFunction) => void} Error middleware */ function errorHandler(options) { const logErrors = (options === null || options === void 0 ? void 0 : options.logErrors) !== false; const includeDetails = (options === null || options === void 0 ? void 0 : options.includeDetails) === true; const logger = (options === null || options === void 0 ? void 0 : options.logger) || (0, logger_utils_1.getLogger)().error; return (err, req, res, _next) => { // Determine status code const status = (err === null || err === void 0 ? void 0 : err.status) || (err === null || err === void 0 ? void 0 : err.statusCode) || http_status_codes_1.HttpStatusCodes.INTERNAL_SERVER_ERROR; // Log error if enabled if (logErrors) { const logMessage = `[ERROR] ${req.method} ${req.originalUrl || req.url}: ${err.message || "Unknown error"}`; logger(logMessage, err); } // Handle ErrorResponse instances (our custom error class) if (err instanceof response_utils_1.ErrorResponse) { return res.status(status).json({ error: err.error, message: err.message, timestamp: err.timestamp, requestId: err.requestId || req.id || (0, id_utils_1.uuid)(), path: req.originalUrl || req.url, }); } // Handle any other errors return res.status(status).json(createErrorResponse((err === null || err === void 0 ? void 0 : err.message) || "Internal Server Error", req, err, { includeDetails, })); }; } //# sourceMappingURL=middleware.utils.js.map