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.

158 lines 6.96 kB
import { randomUUID } from "crypto"; import { Config } from "../config"; import { uuid } from "./id.utils"; import { HttpStatusCodes } from "./http-status-codes"; import { ErrorResponse } from "./response.utils"; import { Env } from "./env.utils"; import { getLogger } from "./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 */ export function requestId(options) { var headerName = (options === null || options === void 0 ? void 0 : options.headerName) || "X-Request-ID"; var exposeHeader = (options === null || options === void 0 ? void 0 : options.exposeHeader) !== false; return function (req, res, next) { // Use existing request ID from header or generate a new one var existingId = req.headers[headerName.toLowerCase()]; var id = existingId || 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 */ export function responseTime(options) { var addHeader = (options === null || options === void 0 ? void 0 : options.addHeader) !== false; var logOnComplete = (options === null || options === void 0 ? void 0 : options.logOnComplete) === true; return function (req, res, next) { var start = process.hrtime(); // Function to calculate elapsed time var calculateDuration = function () { var diff = process.hrtime(start); return diff[0] * 1e3 + diff[1] * 1e-6; // Convert to ms }; // Handle response completion res.on("finish", function () { var duration = calculateDuration(); if (addHeader) { res.setHeader("X-Response-Time", "".concat(duration.toFixed(2), "ms")); } if (logOnComplete) { var method = req.method, url = req.url; getLogger().info("".concat(method, " ").concat(url, " - ").concat(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 */ export function timeout(timeoutMs) { if (timeoutMs === void 0) { timeoutMs = Config.Http.timeout; } return function (req, res, next) { // Set timeout for the request var timer = setTimeout(function () { 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", function () { 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 */ var createErrorResponse = function (message, req, error, options) { var isDev = Env.isDev(); var includeDetails = (options === null || options === void 0 ? void 0 : options.includeDetails) && isDev; var response = { error: true, message: message, timestamp: new Date().toISOString(), requestId: (error === null || error === void 0 ? void 0 : error.requestId) || req.id || 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(function (line) { return 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 */ export function errorHandler(options) { var logErrors = (options === null || options === void 0 ? void 0 : options.logErrors) !== false; var includeDetails = (options === null || options === void 0 ? void 0 : options.includeDetails) === true; var logger = (options === null || options === void 0 ? void 0 : options.logger) || getLogger().error; return function (err, req, res, _next) { // Determine status code var status = (err === null || err === void 0 ? void 0 : err.status) || (err === null || err === void 0 ? void 0 : err.statusCode) || HttpStatusCodes.INTERNAL_SERVER_ERROR; // Log error if enabled if (logErrors) { var logMessage = "[ERROR] ".concat(req.method, " ").concat(req.originalUrl || req.url, ": ").concat(err.message || "Unknown error"); logger(logMessage, err); } // Handle ErrorResponse instances (our custom error class) if (err instanceof ErrorResponse) { return res.status(status).json({ error: err.error, message: err.message, timestamp: err.timestamp, requestId: err.requestId || req.id || 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: includeDetails, })); }; } //# sourceMappingURL=middleware.utils.js.map