/** * 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(), }); // CouchDB document not found if (err.name === "NotFoundError" || err.statusCode === 404) { const message = "Resource not found"; error = new AppError(message, 404); } // CouchDB conflict error (duplicate) if (err.name === "ConflictError" || err.statusCode === 409) { const message = "Duplicate field value entered"; error = new AppError(message, 400); } // CouchDB validation error if (err.name === "ValidationError" || err.statusCode === 400) { const message = err.message || "Validation failed"; 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, };