Show
error class pattern and how to use it for a better, more efficient way of handling errors across your applications.Error handling is one of those parts of software development that don’t quite get the amount of attention it really deserves. However, building robust applications requires dealing with errors properly. You can get by in NodeJS without properly handling errors but due to the asynchronous nature of NodeJS, improper handling or errors can cause you pain soon enough — especially when debugging applications. Before we proceed, I would like to point out the type of errors we’ll be discussing how to utilize error classes. Operational ErrorsThese are errors discovered during the run time of a program. Operational errors are not bugs and can occur from time to time mostly because of one or a combination of several external factors like a database server timing out or a user deciding to make an attempt on SQL injection by entering SQL queries in an input field. Below are more examples of operational errors:
It’s also worthy of note to briefly discuss the counterpart of Operational Errors. Programmer ErrorsThese are bugs in the program which can be resolved by changing the code. These types of errors can not be handled because they occur as a result of the code being broken. Example of these errors are:
This article is about Operational Error handling in NodeJS. Error handling in NodeJS is significantly different from error handling in other languages. This is due to the asynchronous nature of JavaScript and the openness of JavaScript with errors. Let me explain: In JavaScript, instances of the For example, a JavaScript developer may decide to throw in a number instead of an error object instance, like so:
You might not see the problem in throwing other data types, but doing so will result in a harder time debugging because you won’t get a stack trace and other properties that the Error object exposes which are needed for debugging. Let’s look at some incorrect patterns in error handling, before taking a look at the Error class pattern and how it is a much better way for error handling in NodeJS. More after jump! Continue reading below ↓ Bad Error Handling Pattern #1: Wrong Use Of CallbacksReal-world scenario: Your code depends on an external API requiring a callback to get the result you expect it to return. Let’s take the below code snippet:
Until NodeJS 8 and above, the above code was legitimate, and developers would simply fire and forget commands. This means developers weren’t required to provide a callback to such function calls, and therefore could leave out error handling. What happens when the Let’s start solving this problem by solving the race condition. We would do so by giving a callback to the first command
Though we solved the race condition, we are not done quite yet. Our code is
still problematic because even though we used a callback for the first command, we have no way of knowing if the folder Error Handling With CallbacksIn order to handle error properly with callbacks, you must make sure you always use the error-first approach. What this means is that you should first check if there is an error returned from the function before going ahead to use whatever data(if any) was returned. Let’s see the wrong way of doing this:
The above pattern is wrong because sometimes the API you are calling might not return any value or might return a falsy value as a valid return value. This would make you end up in an error case even though you might apparently have a successful call of the function or API. The above pattern is also bad because it’s usage would eat up your error(your errors won’t be called even though it might have happened). You will also have no idea of what is happening in your code as a result of this kind of error handling pattern. So the right way for the above code would be:
Wrong Error Handling Pattern #2: Wrong Use Of PromisesReal-world scenario: So you discovered Promises and you think they are way better than callbacks because of callback hell and you decided on promisifying some external API your code base depended upon. Or you are consuming a promise from an external API or a browser API like the fetch() function. These days we don’t really use callbacks in our NodeJS codebases, we use promises. So let’s reimplement our example code with a promise:
Let’s put the above code under a microscope — we can see that we are branching off the
But the above would not scale. This is because if we have more promise chain to call, we would end up with something similar to the callback hell which promises were made to solve. This means our code will keep indenting to the right. We would have a promise hell on our hands. Promisifying A Callback-Based APIMost times you would want to promisify a callback-based API on your own in order to better handle errors on that API. However, this is not really easy to do. Let’s take an example below to explain why.
From the above, if Swallowed Sync Errors In PromisesUsing the Promise constructor has several difficulties one of these difficulties is; as soon as it is either resolved or rejected it cannot get another state. This is because a promise can only get a single state — either it is pending or it is resolved/rejected. This means we can have dead zones in our promises. Let’s see this in code:
From the above we see as soon as the promise is resolved, the next line is a dead zone and will never be reached. This means any following synchronous error handling perform in your promises will just be swallowed and will never be thrown. Real-World ExamplesThe examples above help explain poor error handling patterns, let’s take a look at the sort of problems you might see in real-life. Real World Example #1 — Transforming Error To StringScenario: You decided the error returned from an API is not really good enough for you so you decided to add your own message to it.
Let’s look at what is wrong with the above code. From the above we see the developer is trying to improve the
error thrown by the A better way is to keep the error as it is or wrap it in another error that you’ve created and attached the thrown error from the databaseGet call as a property to it. Real-World Example #2: Completely Ignoring The ErrorScenario: Perhaps when a user is signing up in your application, if an error occur you want to just catch the error and show a custom message but you completely ignored the error that was caught without even logging it for debugging purposes.
From the above, we can see that the error is completely ignored and the code is sending 500 to the user if the call to the database failed. But in reality, the cause for the database failure might be malformed data sent by the user which is an error with the status code of 400. In the above case, we would be ending up in a debugging horror because you as the developer wouldn’t know what went wrong. The user won’t be able to give a decent report because 500 internal server error is always thrown. You would end up wasting hours in finding the problem which will tantamount to wastage of your employer’s time and money. Real-World Example #3: Not Accepting The Error Thrown From An APIScenario: An error was thrown from an API you were using but you don’t accept that error instead you marshall and transform the error in ways that make it useless for debugging purposes. Take the following code example below:
A lot is going on in the above code that would lead to debugging horror. Let’s take a look:
Below is a better way to write the above code:
Let’s analyze what we are doing right in the above snippet:
TestingWe mostly want to test our code(either manually or automatically). But most times we are only testing for the positive things. For a robust test, you must also test for errors and edge cases. This negligence is responsible for bugs finding their way into production which would cost more extra debugging time. Tip: Always make sure to test not only the positive things(getting a status code of 200 from an endpoint) but also all the error cases and all the edge cases as well. Real-World Example #4: Unhandled RejectionsIf you’ve used promises before, you have probably run into Here is a quick primer on unhandled rejections. Unhandled rejections are promise rejections that weren’t handled. This means that the promise was rejected but your code will continue running. Let’s look at a common real-world example that leads to unhandled rejections..
The above code at first look might seem not error-prone. But on a closer look, we begin to see a defect. Let me explain: What happens when
Here is another real-world scenario that would lead to an unhandled promise rejection error:
If you run the above code
snippet, you will get an unhandled promise rejection, and here is why: Although it’s not obvious, we are returning a promise (foobar) before we are handling it with the
Wrapping Up On The Negative ThingsNow that you have seen wrong error handling patterns, and possible fixes, let’s now dive into Error class pattern and how it solves the problem of wrong error handling in NodeJS. In this pattern, we would start our application with an
Here is how our
This approach enables us to distinguish the errors thrown by our application. So now if we want to handle a bad request error (invalid user input) or a not found error (resource not found) we can inherit from the base class which is
One of the benefits of the You would be able to pass in multiple properties specific to each error class as well during the instantiation of that error. Another key benefit is that you can have properties that are always part of an error class, for example, if you receive a UserFacing error, you would know that a statusCode is always part of this error class now you can just directly use it in the code later on. Tips On Utilizing Error Classes
Let’s see how error classes looks in code. Here is an example in express:
From the above, we are leveraging that Express exposes a global error handler which allows you handle all your errors in one place. You can see the call to So from the above code, to handle our errors we just need to check if the error that was thrown is a You would also notice that in this pattern ( ConclusionProper error handling in your application can make you sleep better at night and save debug time. Here are some takeaway key points to take from this article:
How do you throw an error with a status code?try { //... } catch (e) { console. log(e. statusCode) // not working off course! }. "From the syntax here, it seems we can through the error with additional info:" the page then describes what options should look like. ... . Why do you need to throw Error specifically and not anything else?. How do you throw and catch error in node JS?How Do You Handle Errors in Node.. Use Custom Errors to Handle Operational Errors. ... . Use a Middleware. ... . Restart Your App Gracefully to Handle Programmer Errors. ... . Catch All Uncaught Exceptions. ... . Catch All Unhandled Promise Rejections. ... . Use a Centralized Location for Logs and Error Alerting.. What is throw error in node JS?The throw statement throws a user-defined exception. Execution of the current function will stop (the statements after throw won't be executed), and control will be passed to the first catch block in the call stack. If no catch block exists among caller functions, the program will terminate.
How do I create a custom error in node JS?Simplest way to create a custom error type is to just extend an Error's prototype and initialize the original Error through a constructor:. class MyError extends Error { constructor(message) { super(message) } }. const error = new MyError('problem') console. ... . Error: problem at <anonymous>:1:15.. |