/** * Centralized error handling utility for API routes * * Standard error format: * { * error: 'ErrorType', * message: 'Human-readable description', * field: 'optional-field-name', * code: 'machine-readable-code' * } */ class ApiError extends Error { constructor(code, message, status = 500, details = {}) { super(message); this.name = 'ApiError'; this.code = code; this.status = status; this.details = details; // Extract field name from details if provided if (details.field) { this.field = details.field; } } } /** * Create a standardized validation error */ function ValidationError(message, field, code = 'VALIDATION_ERROR') { return new ApiError(code, message, 400, { field }); } /** * Create a standardized authentication error */ function AuthError(message = 'Authentication required', code = 'AUTH_ERROR') { return new ApiError(code, message, 401); } /** * Create a standardized authorization error */ function ForbiddenError(message = 'Access denied', code = 'FORBIDDEN') { return new ApiError(code, message, 403); } /** * Create a standardized not found error */ function NotFoundError(message = 'Resource not found', code = 'NOT_FOUND') { return new ApiError(code, message, 404); } /** * Create a standardized conflict error */ function ConflictError(message = 'Resource conflict', code = 'CONFLICT') { return new ApiError(code, message, 409); } /** * Create a standardized rate limit error */ function RateLimitError(message = 'Too many requests', code = 'RATE_LIMITED') { return new ApiError(code, message, 429); } /** * Format an error for JSON response * This ensures consistent error format across all routes */ function formatError(err) { if (err instanceof ApiError) { const output = { error: err.code, message: err.message, }; if (err.field) output.field = err.field; return output; } // Fallback for non-standard errors (log internally, show safe message) const safeMessage = err.status === 500 ? 'Internal server error' : err.message || 'An error occurred'; return { error: err.code || 'ERROR', message: safeMessage, }; } /** * Express middleware to handle errors centrally */ function errorHandler(err, req, res, next) { const formatted = formatError(err); const status = err.status || (err instanceof ApiError ? 500 : 500); // Only log non-500 errors to avoid exposing sensitive info if (status !== 500) { console.error(`[error] ${formatted.error}: ${formatted.message}`); } if (!res.headersSent) { res.status(status).json(formatted); } } module.exports = { ApiError, ValidationError, AuthError, ForbiddenError, NotFoundError, ConflictError, RateLimitError, formatError, errorHandler, };