import { Request, Response, NextFunction } from "express"; import { logger } from "../logging/logger"; /** * Error classification */ export enum ErrorType { USER_ERROR = "USER_ERROR", SYSTEM_ERROR = "SYSTEM_ERROR", VALIDATION_ERROR = "VALIDATION_ERROR", AUTHENTICATION_ERROR = "AUTHENTICATION_ERROR", AUTHORIZATION_ERROR = "AUTHORIZATION_ERROR", NOT_FOUND_ERROR = "NOT_FOUND_ERROR", RATE_LIMIT_ERROR = "RATE_LIMIT_ERROR", EXTERNAL_SERVICE_ERROR = "EXTERNAL_SERVICE_ERROR", } /** * Custom error class */ export class AppError extends Error { constructor( public type: ErrorType, public statusCode: number, message: string, public details?: any ) { super(message); this.name = "AppError"; } } /** * Error handling middleware */ export function errorHandler( err: Error | AppError, req: Request, res: Response, next: NextFunction ) { const requestId = req.headers["x-request-id"] as string || "unknown"; // Handle known application errors if (err instanceof AppError) { logger.warn({ error: err, type: err.type, requestId, path: req.path, }, `Application error: ${err.message}`); return res.status(err.statusCode).json({ error: err.type, message: err.message, details: err.details, requestId, }); } // Handle validation errors if (err.name === "ValidationError" || err.name === "ZodError") { logger.warn({ error: err, requestId, path: req.path, }, "Validation error"); return res.status(400).json({ error: ErrorType.VALIDATION_ERROR, message: "Validation failed", details: err.message, requestId, }); } // Handle unknown errors logger.error({ error: err, requestId, path: req.path, stack: err.stack, }, "Unhandled error"); res.status(500).json({ error: ErrorType.SYSTEM_ERROR, message: "An internal server error occurred", requestId, ...(process.env.NODE_ENV === "development" && { details: err.message }), }); } /** * Async error wrapper */ export function asyncHandler( fn: (req: Request, res: Response, next: NextFunction) => Promise ) { return (req: Request, res: Response, next: NextFunction) => { Promise.resolve(fn(req, res, next)).catch(next); }; }