/** * Centralized Error Handling Utility * * This module provides standardized error handling patterns across the application, * ensuring consistent error responses, proper logging, and appropriate error * classification. It implements error handling best practices including: * * 1. Standardized error response format for API consistency * 2. Error classification for appropriate handling strategies * 3. Context-aware logging for debugging and monitoring * 4. Request ID tracking for error correlation * 5. Severity-based error routing and alerting * * Design Rationale: * - Centralizes error handling logic to reduce code duplication * - Provides consistent user experience across all endpoints * - Enables comprehensive error monitoring and debugging * - Supports multiple error handling strategies based on error type * - Maintains backward compatibility with existing error patterns */ const { qerrors } = require('qerrors'); const { sendJsonResponse } = require('./responseUtils'); const { getRequestId } = require('./requestUtils'); /** * Error type classification for appropriate handling strategies * * Rationale: Different error types require different handling approaches. * This classification enables appropriate response codes, user messages, * logging levels, and recovery strategies. */ const ErrorTypes = { VALIDATION: 'validation', // User input errors (400) AUTHENTICATION: 'authentication', // Auth failures (401) AUTHORIZATION: 'authorization', // Permission errors (403) NOT_FOUND: 'not_found', // Resource not found (404) RATE_LIMIT: 'rate_limit', // Rate limiting (429) NETWORK: 'network', // External service errors (502/503) DATABASE: 'database', // Database errors (500) SYSTEM: 'system', // Internal system errors (500) CONFIGURATION: 'configuration' // Config/setup errors (500) }; /** * Error severity levels for logging and alerting * * Rationale: Different error severities require different response strategies. * This classification enables appropriate logging, alerting, and escalation. */ const ErrorSeverity = { LOW: 'low', // Expected errors, user mistakes MEDIUM: 'medium', // Operational issues, recoverable HIGH: 'high', // Service degradation, requires attention CRITICAL: 'critical' // Service disruption, immediate response needed }; /** * Maps error types to appropriate HTTP status codes * * Rationale: Consistent HTTP status code mapping ensures proper client * behavior and follows REST API conventions. */ const ERROR_STATUS_MAP = { [ErrorTypes.VALIDATION]: 400, [ErrorTypes.AUTHENTICATION]: 401, [ErrorTypes.AUTHORIZATION]: 403, [ErrorTypes.NOT_FOUND]: 404, [ErrorTypes.RATE_LIMIT]: 429, [ErrorTypes.NETWORK]: 502, [ErrorTypes.DATABASE]: 500, [ErrorTypes.SYSTEM]: 500, [ErrorTypes.CONFIGURATION]: 500 }; /** * Maps error types to severity levels for monitoring * * Rationale: Automatic severity classification enables appropriate * alerting and escalation without manual intervention. */ const ERROR_SEVERITY_MAP = { [ErrorTypes.VALIDATION]: ErrorSeverity.LOW, [ErrorTypes.AUTHENTICATION]: ErrorSeverity.LOW, [ErrorTypes.AUTHORIZATION]: ErrorSeverity.MEDIUM, [ErrorTypes.NOT_FOUND]: ErrorSeverity.LOW, [ErrorTypes.RATE_LIMIT]: ErrorSeverity.MEDIUM, [ErrorTypes.NETWORK]: ErrorSeverity.MEDIUM, [ErrorTypes.DATABASE]: ErrorSeverity.HIGH, [ErrorTypes.SYSTEM]: ErrorSeverity.HIGH, [ErrorTypes.CONFIGURATION]: ErrorSeverity.CRITICAL }; /** * Creates a standardized error object with consistent format * * Rationale: Standardized error format ensures consistent API responses * and enables proper error handling on the client side. Includes all * necessary information for debugging and user feedback. * * @param {string} code - Error code for programmatic handling * @param {string} message - Human-readable error message * @param {string} type - Error type from ErrorTypes enum * @param {Object} context - Additional context for debugging * @returns {Object} Standardized error object */ function createError(code, message, type, context = {}) { // build standard error object return { code, message, type, timestamp: new Date().toISOString(), requestId: context.requestId || getRequestId(context.req), context: { ...context, req: undefined, // Remove req object to prevent circular references res: undefined // Remove res object to prevent circular references } }; } /** * Logs error with appropriate severity and context * * Rationale: Centralized error logging ensures consistent log format * and enables proper monitoring and alerting. Different severities * can be routed to different logging destinations. * * @param {Object} error - Error object or Error instance * @param {string} functionName - Name of function where error occurred * @param {Object} context - Request context and additional information * @param {string} severity - Error severity level */ function logError(error, functionName, context = {}, severity = ErrorSeverity.MEDIUM) { // log with severity context const logContext = { ...context, severity, timestamp: new Date().toISOString(), requestId: context.requestId || getRequestId(context.req) }; // Use existing qerrors for consistent logging qerrors(error, functionName, logContext); // Additional logging based on severity if (severity === ErrorSeverity.CRITICAL) { console.error(`CRITICAL ERROR in ${functionName}:`, { error: error.message || error, context: logContext }); } else if (severity === ErrorSeverity.HIGH) { console.error(`HIGH SEVERITY ERROR in ${functionName}:`, { error: error.message || error, context: logContext }); } } /** * Handles controller errors with standardized response * * Rationale: Provides consistent error handling across all controllers * while maintaining existing responseUtils integration. Automatically * determines appropriate status codes and response format. * * @param {Object} res - Express response object * @param {Object} error - Error object or Error instance * @param {string} functionName - Name of function where error occurred * @param {Object} context - Request context * @param {string} userMessage - Optional user-friendly message override */ function handleControllerError(res, error, functionName, context = {}, userMessage = null) { // send standardized error response const errorType = error.type || ErrorTypes.SYSTEM; const severity = ERROR_SEVERITY_MAP[errorType]; const statusCode = ERROR_STATUS_MAP[errorType]; // Log the error with appropriate severity logError(error, functionName, context, severity); // Create standardized error response const errorResponse = createError( error.code || 'INTERNAL_ERROR', userMessage || error.message || 'An internal error occurred', errorType, context ); // Send standardized JSON response sendJsonResponse(res, statusCode, { error: errorResponse }); } /** * Wraps async operations with standardized error handling * * Rationale: Reduces boilerplate code in controllers while ensuring * consistent error handling. Automatically catches and handles errors * according to their type and severity. * * @param {Function} operation - Async operation to execute * @param {string} functionName - Name for logging purposes * @param {Object} context - Request context * @param {*} fallback - Fallback value on error (optional) * @returns {*} Operation result or fallback value */ async function withErrorHandling(operation, functionName, context = {}, fallback = null) { // execute operation with safety net try { const result = await operation(); console.log(`${functionName} is returning result`); return result; } catch (error) { logError(error, functionName, context); return fallback; } }