DEV Community

Abu Bakar Wase
Abu Bakar Wase

Posted on

Global Error Handling

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
  })
}
Enter fullscreen mode Exit fullscreen mode

-: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

  1. Invalid object_Id (Cast Error)
  2. Duplicate key (Duplicate entry/ unique field restraint)
  3. 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}
      }
Enter fullscreen mode Exit fullscreen mode

-: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

  1. We are going to make an ErrorResponse class
  2. We will pass err to the next function like next(err)
  3. 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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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)
}
Enter fullscreen mode Exit fullscreen mode

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)
      );
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
arslanarsn profile image
Muhammad Arslan

Well Explained. keep up the work.