qerrors
Version:
Intelligent error handling middleware with AI-powered analysis, environment validation, caching, and production-ready logging. Provides OpenAI-based error suggestions, queue management, retry mechanisms, and comprehensive configuration options for Node.js
214 lines (200 loc) • 8.28 kB
Plain Text
/**
* 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;
}
}