Error Handling in Nodejs : Part-1
In this article, we will see different types of errors that can happens in application development and what are the ideal ways to deal with them in Nodejs.
So, let's get started...
How to do Error Handling in Nodejs?
In the Programming world there is basically main two types of error that can happen:
1) Operational Errors
Operational Errors are those problems that we can predict will happen at some point, so we just need to handle them in advance.
Below we have mentioned some examples of it:
🩸 User accessing some invalid routes
🩸 User entering invalid data in the input
🩸 Database connection failed
etc...
2) Programming Errors
Programming Errors are bugs that we developers introduced into our code by mistackes or unknowingly, which is difficult to find and handle.
Some examples of it are:
🩸 Trying to read undefined properties
🩸 Using await without async
etc...
Handling Unhandled Routes:
When a User hits a URL that doesn't exist, we can consider this as an Operational error which we must have to handle in advance.
Suppose you have develpes some API's in your Nodejs Application, now if someone tries to access your api with some invalid url for example your API is url is <your_domain>/users and if we enter like <your_domain>/usersList, so in Postman you will get something like this:
However, as we are using API so it doesn't make sense to send back HTML instead of JSON.
Let's fix it,
here we have to create a handler function to handle these types of routes.
In nodejs, all the middlware are executed in the order as they are in the code, so the idea is to if we have a request that makes it to this point of our code, that means no routes have matched, so if we add middleware there after hthese routes it will be only reached again if not handled by any route.
So, let's create one handler function after all routes, as below:
All routes will come here
---------
// Handling Unhandled Routes
app.all("*", (req, res, next) => {
res.status(404).json({
status: "fail",
message: `Can't find ${req.originalUrl} Url`,
});
});
In above the app.all()
the method will catch, all the incoming requests (Get, Post,Put, delete,etc..), so if there is no route match from above-listed routes then it will come to this handler and return this type of error, now it will look like this:
Also read,
Global Error Handling Middleware
Here we are going to create a global middleware that will handle Operational errors like the above.
Now let's do this in better way instead of defining it on one place, as we might also need to handle this situaltion other place.
Le's create an global middalware that can handle all this in one place.
To define error handling middleware all we need to is to give the middleware function four arguments and express will then automatically recognize it as an error handling middleware, so it will only be called if there is any error.
// Global Error Handling
app.use((err, req, res, next) => {
err.statusCode = err.statusCode || 500;
err.status = err.status || "error";
res.status(err.statusCode).json({
status: err.status,
message: err.message,
});
});
this middlware function is error first function,which means first argument will be error
Now we have Global Error handling middlawre is created, but to make sure is it working or not ? we need to create an error, so let's do it:
// Handling Unhandled Routes
app.all("*", (req, res, next) => {
// Manually Create error for testing
const err = new Error(`Can't find ${req.originalUrl} Url`);
err.status = "fail";
err.statusCode = 404; */
next(err);
});
In Above snippets we have used buil in Error constrctor to manually create error, then we defined the status and statusCode properties on it so that our error handling middlware can use them in the next step.
After that, we have passed the error in the next function, and remember whenever we pass anything into the next, it will assume that it is an error, and it will then skip all the other middleware in the middleware stack and sent the error that we passed into our global error handling middleware.
Now let's create a seprate custom class to handle these errors in a better way.
Create a utils directory and add the appError.js file.
Here we are using our built-in Error class by expanding it, which is called inheritance.
Constructor(): Constructor gets called each time when we create a new object from the class.
Super(): When we extend a parent class, we need to call super in order to call the parent constructor.
So,now onwards we wil use this new AppError class to create manualy error in our application insttead of built in Error class.
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.status = `${statusCode}`.startsWith("4") ? "fail" : "error";
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
module.exports = AppError;
Create an ErrorController.js file in the Controller folder.
moduel.exports = (err, req, res, next) => {
err.statusCode = err.statusCode || 500;
err.status = err.status || "error";
res.status(err.statusCode).json({
status: err.status,
message: err.message,
});
}
Now import these two files into your main index.js file and update it like this:
// Handling Unhandled Routes
app.all("*", (req, res, next) => {
next(new AppError(`Can't find ${req.originalUrl} Url`, 404));
});
// Global Error Handling
app.use(globalErrorHandler);
Now Test again and you will get same result, but now our code is more refators and easy to read, right!.
That's it for this article, in our very next article we will take it to more advanced, so stay tuned.