/** * Centralized Error Handling Middleware * Handles all errors throughout the application with consistent formatting */ // Custom error class for application-specific errors class AppError extends Error { constructor(message, statusCode) { super(message); this.statusCode = statusCode; this.isOperational = true; Error.captureStackTrace(this, this.constructor); } } // Global error handler middleware const errorHandler = (err, req, res, next) => { let error = { ...err }; error.message = err.message; // Log error for debugging console.error(`[ERROR] ${err.message}`, { stack: err.stack, path: req.path, method: req.method, timestamp: new Date().toISOString(), }); // Mongoose bad ObjectId if (err.name === "CastError") { const message = "Resource not found"; error = new AppError(message, 404); } // Mongoose duplicate key if (err.code === 11000) { const message = "Duplicate field value entered"; error = new AppError(message, 400); } // Mongoose validation error if (err.name === "ValidationError") { const message = Object.values(err.errors) .map((val) => val.message) .join(", "); error = new AppError(message, 400); } // JWT errors if (err.name === "JsonWebTokenError") { const message = "Invalid token"; error = new AppError(message, 401); } if (err.name === "TokenExpiredError") { const message = "Token expired"; error = new AppError(message, 401); } // Send error response res.status(error.statusCode || 500).json({ success: false, error: error.message || "Server Error", ...(process.env.NODE_ENV === "development" && { stack: err.stack }), }); }; // Async handler wrapper to catch errors in async route handlers const asyncHandler = (fn) => (req, res, next) => { Promise.resolve(fn(req, res, next)).catch(next); }; module.exports = { errorHandler, asyncHandler, AppError, };