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
448 lines (421 loc) • 19.4 kB
JavaScript
/**
* Error classification and standardized handling utilities for qerrors
*
* This module provides standardized error handling patterns extending the core qerrors
* functionality with structured error classification, severity mapping, and Express-specific
* response utilities. 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:
* - Extends existing qerrors functionality rather than replacing it
* - Provides consistent error classification across applications
* - Enables appropriate HTTP status code mapping
* - Supports severity-based monitoring and alerting
* - Maintains backward compatibility with existing qerrors usage
*/
; //(enable strict mode for error types module)
const crypto = require('crypto'); //node crypto for request ID generation
const { randomUUID } = require('crypto'); //import UUID generator for request tracking
/**
* Error type classification for appropriate handling strategies
*
* Design 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
*
* Design 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
*
* Design 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
*
* Design 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
};
/**
* Extracts or generates request ID for error correlation
*
* Design rationale: Request tracking enables correlation of errors across
* distributed systems and helps with debugging user-specific issues.
*
* @param {Object} req - Express request object (optional)
* @returns {string} Request ID for correlation
*/
function getRequestId(req) { //extract or generate request identifier for tracking
if (req && req.headers) { //extract from headers when available
return req.headers['x-request-id'] ||
req.headers['x-correlation-id'] ||
req.headers['request-id'] ||
randomUUID(); //generate when header missing
}
return randomUUID(); //generate when no request object
}
/**
* Creates a standardized error object with consistent format
*
* Design 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 createStandardError(code, message, type, context = {}) { //build standard error object with consistent format
return {
code, //error code for programmatic handling
message, //human readable error description
type, //classification from ErrorTypes enum
timestamp: new Date().toISOString(), //ISO timestamp for chronological analysis
requestId: context.requestId || getRequestId(context.req), //correlation identifier
context: { //additional debugging information
...context,
req: undefined, //remove req object to prevent circular references in JSON
res: undefined //remove res object to prevent circular references in JSON
}
};
}
/**
* Sends standardized JSON error response
*
* Design rationale: Centralized response logic ensures consistent API
* behavior and reduces code duplication across controllers.
*
* @param {Object} res - Express response object
* @param {number} statusCode - HTTP status code
* @param {Object} errorObject - Standardized error object
*/
function sendErrorResponse(res, statusCode, errorObject) { //send consistent JSON error response
if (res && !res.headersSent) { //prevent double response errors
res.status(statusCode).json({ error: errorObject }); //structured error response format
}
}
/**
* Creates a typed error with classification and context
*
* Design rationale: Factory function for creating errors with proper
* classification, enabling consistent handling across the application.
*
* @param {string} message - Error message
* @param {string} type - Error type from ErrorTypes
* @param {string} code - Error code for identification
* @param {Object} context - Additional context
* @returns {Error} Enhanced error object with type information
*/
function createTypedError(message, type, code = 'GENERIC_ERROR', context = {}) { //create error with type classification
const error = new Error(message); //base error object
error.type = type; //attach error type for handling logic
error.code = code; //attach error code for programmatic identification
error.context = context; //attach context for debugging
error.statusCode = ERROR_STATUS_MAP[type] || 500; //determine HTTP status from type
error.severity = ERROR_SEVERITY_MAP[type] || ErrorSeverity.MEDIUM; //determine severity from type
return error; //return enhanced error object
}
/**
* Factory for creating specific error types with predefined configurations
*
* Design rationale: Provides convenient error creation functions for common
* error scenarios, ensuring consistent error codes and messages across the
* application while reducing boilerplate code.
*/
const ErrorFactory = {
/**
* Creates validation error for user input issues
*
* @param {string} message - Validation error message
* @param {string} field - Optional field name that failed validation
* @param {Object} context - Additional context for debugging
* @returns {Object} Standardized validation error object
*/
validation(message, field = null, context = {}) { //create validation error with field context
return createStandardError(
'VALIDATION_ERROR', //consistent validation error code
message, //user-provided validation message
ErrorTypes.VALIDATION, //validation error type
{ ...context, field } //include field context for debugging
);
},
/**
* Creates authentication error for login/auth issues
*
* @param {string} message - Auth error message
* @param {Object} context - Additional context for debugging
* @returns {Object} Standardized authentication error object
*/
authentication(message = 'Authentication required', context = {}) { //create auth error with default message
return createStandardError(
'AUTHENTICATION_ERROR', //consistent auth error code
message, //auth failure message
ErrorTypes.AUTHENTICATION, //authentication error type
context //debugging context
);
},
/**
* Creates authorization error for permission issues
*
* @param {string} message - Authorization error message
* @param {Object} context - Additional context for debugging
* @returns {Object} Standardized authorization error object
*/
authorization(message = 'Insufficient permissions', context = {}) { //create authz error with default message
return createStandardError(
'AUTHORIZATION_ERROR', //consistent authz error code
message, //permission failure message
ErrorTypes.AUTHORIZATION, //authorization error type
context //debugging context
);
},
/**
* Creates not found error for missing resources
*
* @param {string} resource - Name of the missing resource
* @param {Object} context - Additional context for debugging
* @returns {Object} Standardized not found error object
*/
notFound(resource, context = {}) { //create not found error with resource context
return createStandardError(
'NOT_FOUND', //consistent not found error code
`${resource} not found`, //resource-specific not found message
ErrorTypes.NOT_FOUND, //not found error type
context //debugging context
);
},
/**
* Creates rate limit error for quota violations
*
* @param {string} message - Rate limit error message
* @param {Object} context - Additional context for debugging
* @returns {Object} Standardized rate limit error object
*/
rateLimit(message = 'Rate limit exceeded', context = {}) { //create rate limit error with default message
return createStandardError(
'RATE_LIMIT_EXCEEDED', //consistent rate limit error code
message, //rate limit violation message
ErrorTypes.RATE_LIMIT, //rate limit error type
context //debugging context
);
},
/**
* Creates network error for external service issues
*
* @param {string} message - Network error message
* @param {string} service - Optional name of the external service
* @param {Object} context - Additional context for debugging
* @returns {Object} Standardized network error object
*/
network(message, service = null, context = {}) { //create network error with service context
return createStandardError(
'NETWORK_ERROR', //consistent network error code
message, //network failure message
ErrorTypes.NETWORK, //network error type
{ ...context, service } //include service context for debugging
);
},
/**
* Creates database error for data persistence issues
*
* @param {string} message - Database error message
* @param {string} operation - Optional database operation that failed
* @param {Object} context - Additional context for debugging
* @returns {Object} Standardized database error object
*/
database(message, operation = null, context = {}) { //create database error with operation context
return createStandardError(
'DATABASE_ERROR', //consistent database error code
message, //database failure message
ErrorTypes.DATABASE, //database error type
{ ...context, operation } //include operation context for debugging
);
},
/**
* Creates system error for internal issues
*
* @param {string} message - System error message
* @param {string} component - Optional system component that failed
* @param {Object} context - Additional context for debugging
* @returns {Object} Standardized system error object
*/
system(message, component = null, context = {}) { //create system error with component context
return createStandardError(
'SYSTEM_ERROR', //consistent system error code
message, //system failure message
ErrorTypes.SYSTEM, //system error type
{ ...context, component } //include component context for debugging
);
}
};
/**
* Express middleware for global error handling with improved error safety
*
* Design rationale: Catches any unhandled errors in the Express middleware chain
* and ensures they are properly logged and responded to with consistent format.
* This provides a safety net for the entire application while avoiding circular dependencies.
* Includes meta-error handling to prevent complete system failure.
*
* @param {Error} error - Error object from Express middleware chain
* @param {Object} req - Express request object
* @param {Object} res - Express response object
* @param {Function} next - Express next function
*/
function errorMiddleware(error, req, res, next) { //global Express error middleware with enhanced safety
try {
const context = { //build request context for debugging
req, //include request object for qerrors analysis
url: req.url, //capture request URL
method: req.method, //capture HTTP method
ip: req.ip, //capture client IP for tracking
userAgent: req.headers['user-agent'] //capture user agent for debugging
};
const errorType = error.type || ErrorTypes.SYSTEM; //default to system error when type missing
const severity = ERROR_SEVERITY_MAP[errorType]; //determine severity from error type
const statusCode = ERROR_STATUS_MAP[errorType]; //determine HTTP status from error type
// Create standardized error response object
const errorResponse = createStandardError(
error.code || 'INTERNAL_ERROR', //error code for programmatic handling
error.message || 'An internal error occurred', //error message for display
errorType, //error classification
context //debugging context
);
// Only send response if headers haven't been sent already
// This prevents "Cannot set headers after they are sent" errors
if (!res.headersSent) {
sendErrorResponse(res, statusCode, errorResponse);
}
} catch (metaError) {
// Handle meta-errors (errors in error handling itself)
// This provides a fallback to prevent complete system failure
console.error('Meta-error in errorMiddleware:', metaError.message); //log meta-error for debugging
if (!res.headersSent) {
// Send minimal error response as last resort
try {
res.status(500).json({
error: {
code: 'SYSTEM_ERROR',
message: 'An internal error occurred',
timestamp: new Date().toISOString()
}
});
} catch (finalError) {
// Ultimate fallback - just end the response
console.error('Final error in errorMiddleware:', finalError.message);
if (!res.headersSent) {
res.status(500).end();
}
}
}
}
}
/**
* Simplified error handler for basic error responses
*
* Design rationale: Provides a lightweight alternative to the full handleControllerError
* for cases where simple error responses are needed without the full classification system.
* This matches the pattern shown in legacy code while maintaining qerrors integration.
*
* @param {Object} res - Express response object
* @param {Error} error - The error object that occurred
* @param {string} message - Human-readable error message for the client
* @param {Object} req - Optional Express request object for additional context
*/
function handleSimpleError(res, error, message, req) { //simplified error response handler
try {
// Log error through qerrors with appropriate context
const qerrors = require('./qerrors'); //load qerrors for logging
if (req) {
qerrors(error, message, req); //log error with full request context for better debugging
} else {
qerrors(error, message); //log error without request context for background operations
}
// Only send response if headers haven't been sent already
// This prevents "Cannot set headers after they are sent" errors
if (!res.headersSent) {
const errorResponse = createStandardError(
'INTERNAL_ERROR', //error code for programmatic handling
message, //user-provided error message
ErrorTypes.SYSTEM, //default to system error type
{} //empty context for simple errors
);
sendErrorResponse(res, 500, errorResponse);
}
} catch (metaError) {
// Handle meta-errors (errors in error handling itself)
// This provides a fallback to prevent complete system failure
console.error('Meta-error in handleSimpleError:', metaError.message); //log meta-error for debugging
if (!res.headersSent) {
try {
res.status(500).json({ error: message }); //minimal fallback response
} catch (finalError) {
console.error('Final error in handleSimpleError:', finalError.message);
res.status(500).end(); //ultimate fallback
}
}
}
}
module.exports = { //(export error handling utilities for use across qerrors module)
ErrorTypes, //(error classification constants)
ErrorSeverity, //(severity level constants)
ERROR_STATUS_MAP, //(type to HTTP status mapping)
ERROR_SEVERITY_MAP, //(type to severity mapping)
getRequestId, //(request ID extraction utility)
createStandardError, //(standardized error object factory)
sendErrorResponse, //(consistent response utility)
createTypedError, //(typed error factory function)
ErrorFactory, //(convenient error creation utilities)
errorMiddleware, //(Express global error handling middleware)
handleSimpleError //(simplified error response handler)
};