There are several ways to handle error in Node application with express server in action, most famous of them is using try catch
block
try {
// some async code/execution
} catch (err) {
// throwing error like
res.status(error_code).json({
success: false,
error: "any_custom_error_message" || err.message
})
}
-:Issues with this approach:-
Usually in applications we have some very common errors which are appearing due to very same reason in multiple functions. Some examples are
- Invalid object_Id (Cast Error)
- Duplicate key (Duplicate entry/ unique field restraint)
- Validation Errors (required field error)
Usually validation errors appear due to restraints which we put on keys in our model.
name: {
required: [true, "Name is required}
}
-:What we are going to do:-
We are going to make a global error handler which will handle these repeatedly occurring errors in a very DRY(Do not repeat yourself) way.
Steps we are going to follow
- We are going to make an ErrorResponse class
- We will pass err to the next function like
next(err)
- We will make a middle-ware function which will handle these errors
-:Lets do some code:-
class ErrorResponse extends Error {
constructor(message, statusCode) {
super(message); // we do this because Error class has its own error.message property and we are going to pass our message to that property
this.statusCode = statusCode;
}
}
module.exports = ErrorResponse;
You can create this class in utils or any helper folder. basically this will instantiate the global Error class of Javascript with our own custom message and status code.
Now second step is to make a error.js
middle-ware which will handle responses on err
properties like
const ErrorResponse = require("../utils/errorResponse");
const errorHandler = (err, req, res, next) => {
let error = { ...err };
error.message = err.message;
// Log to console for developers
console.log(err.stack.red);
// Mongoose bad ObjectId
if (err.name === "CastError") {
const message = `Resource not found with id of ${err.value}`;
error = new ErrorResponse(message, 404);
}
// Mongoose duplicate key
if (err.code === 11000) {
const message = "Duplicate field value entered";
error = new ErrorResponse(message, 400);
}
// Mongoose validation error
if (err.name === "ValidatorError") {
const message = Object.values(err.error).map(val => val.message);
error = new ErrorResponse(message, 400);
}
res.status(error.statusCode || 500).json({
success: false,
error: error.message || "Server Error"
});
};
module.exports = errorHandler;
This is basically going to handle those three most repeatedly occurring responses on the base of err.name
and err.code
properties.
Third step is to change your controller methods like
try{
//async code
} catch (err) {
next(err)
}
and in the end mount error.js
middle-ware after app.use(routes)
Now you don't need to handle these three most commonly occurring errors again and again in your controller functions. You can also use ErrorResponse class to handle case of no resource found and any other function.
if (!resource)
return next(
new ErrorResponse(`Resource not found with id of ${req.params.id}`, 404)
);
Top comments (1)
Well Explained. keep up the work.