/** * Error Handling Middleware */ import { Request, Response, NextFunction } from "express"; import { createLogger } from "../core/index.js"; const logger = createLogger("ErrorHandler"); // Custom error class export class AppError extends Error { public readonly statusCode: number; public readonly isOperational: boolean; constructor( message: string, statusCode: number = 500, isOperational: boolean = true, ) { super(message); this.statusCode = statusCode; this.isOperational = isOperational; Error.captureStackTrace(this, this.constructor); } } // 404 Not Found handler export function notFoundHandler(req: Request, res: Response) { res.status(404).json({ success: false, error: "Not Found", message: `Cannot ${req.method} ${req.path}`, path: req.path, method: req.method, }); } // Global error handler export function errorHandler( err: any, req: Request, res: Response, _next: NextFunction, ) { // Default to 500 server error let statusCode = err.statusCode || 500; let message = err.message || "Internal Server Error"; // Log the error if (statusCode >= 500) { logger.error("Server error", { error: err, request: { method: req.method, path: req.path, query: req.query, body: req.body, headers: req.headers, }, }); } else { logger.warn("Client error", { statusCode, message, path: req.path, }); } // Handle specific error types if (err.name === "ValidationError") { statusCode = 400; message = "Validation failed"; } else if (err.name === "UnauthorizedError") { statusCode = 401; message = "Unauthorized"; } else if (err.name === "JsonWebTokenError") { statusCode = 401; message = "Invalid token"; } else if (err.name === "TokenExpiredError") { statusCode = 401; message = "Token expired"; } // Send error response res.status(statusCode).json({ success: false, error: message, ...(process.env.NODE_ENV === "development" && { stack: err.stack, details: err, }), }); } // Async handler wrapper to catch errors in async routes export function asyncHandler( fn: (req: Request, res: Response, next: NextFunction) => any, ) { return (req: Request, res: Response, next: NextFunction) => { Promise.resolve(fn(req, res, next)).catch(next); }; }