Problem Statement
Currently, the backend codebase lacks a unified strategy for handling exceptions. Error handling is likely performed locally within individual controllers using repetitive try-catch blocks. This results in code duplication, inconsistent API error response structures (e.g., varying field names for error messages), and difficulty in debugging due to unmanaged stack traces. To maintain a robust backend architecture, we need to standardize how errors are generated and sent to the client.
Current Behavior / Limitation
- Inconsistency: Some controllers may return
{ error: "message" }, while others return { message: "error" } or just a plain string.
- Redundancy: Developers have to manually set HTTP status codes and write JSON response logic in every
catch block.
- Visibility: Stack traces might be inadvertently exposed in production or missing in development environments where they are needed for debugging.
Expected Improvement
After this refactor, the application should utilize a global error handling mechanism. Controllers will simply pass errors to the next() function. The API will produce a consistent error contract:
{
"status": "fail", // or "error"
"message": "Resource not found",
"stack": "..." // Only present in development mode
}
Proposed Approach
-
Create server/utils/AppError.js:
- Create a custom class extending the native JS
Error class.
- It should accept
message and statusCode in the constructor.
- Set
this.isOperational = true (to distinguish operational errors from programming bugs).
-
Create server/middleware/errorMiddleware.js:
- Implement a standard Express error handling function signature:
(err, req, res, next).
- Default the status code to 500 if not set.
- Development Mode: Send
statusCode, status, message, and stack.
- Production Mode: Send clean error messages. Do not leak stack traces.
-
Update server/index.js:
- Import and use the global error handler middleware after all route declarations.
- Add a handler for unhandled routes (404) that forwards an
AppError to the global handler.
-
Refactor Controllers (server/controllers/*.js):
- Replace manual
res.status(x).json(...) error responses with next(new AppError('Message', statusCode)).
Verification Steps
To verify that the centralized error handling is working correctly, follow these steps:
- Setup: Ensure the server is running (
npm run dev or npm start).
- Test 404 (Not Found):
- Open Postman or your browser.
- Send a request to a non-existent route, e.g.,
GET http://localhost:5000/api/non-existent-route.
- Verify: You receive a JSON response with status 404 and a standard error message.
- Test Operational Error:
- Temporarily modify a controller (e.g., in
authController) to throw a new AppError('Test error', 400).
- Trigger that controller endpoint.
- Verify: The response is a 400 Bad Request with your custom message.
- Test Environment Logic:
- Check the response body. If
NODE_ENV=development, you should see the stack property.
- Set
NODE_ENV=production in your .env file and restart the server.
- Trigger the error again.
- Verify: The
stack property is absent from the response.
Labels: ECWoC26
Problem Statement
Currently, the backend codebase lacks a unified strategy for handling exceptions. Error handling is likely performed locally within individual controllers using repetitive
try-catchblocks. This results in code duplication, inconsistent API error response structures (e.g., varying field names for error messages), and difficulty in debugging due to unmanaged stack traces. To maintain a robust backend architecture, we need to standardize how errors are generated and sent to the client.Current Behavior / Limitation
{ error: "message" }, while others return{ message: "error" }or just a plain string.catchblock.Expected Improvement
After this refactor, the application should utilize a global error handling mechanism. Controllers will simply pass errors to the
next()function. The API will produce a consistent error contract:{ "status": "fail", // or "error" "message": "Resource not found", "stack": "..." // Only present in development mode }Proposed Approach
Create
server/utils/AppError.js:Errorclass.messageandstatusCodein the constructor.this.isOperational = true(to distinguish operational errors from programming bugs).Create
server/middleware/errorMiddleware.js:(err, req, res, next).statusCode,status,message, andstack.Update
server/index.js:AppErrorto the global handler.Refactor Controllers (
server/controllers/*.js):res.status(x).json(...)error responses withnext(new AppError('Message', statusCode)).Verification Steps
To verify that the centralized error handling is working correctly, follow these steps:
npm run devornpm start).GET http://localhost:5000/api/non-existent-route.authController) to throw a newAppError('Test error', 400).NODE_ENV=development, you should see thestackproperty.NODE_ENV=productionin your.envfile and restart the server.stackproperty is absent from the response.Labels: ECWoC26