Can you name any programming language that is without an error?

You can’t. Actually, even the highly experienced developers cannot do that.

nodejs-error-handling-system-main

It is because every programming language has some or the other error in it, and that is why we have so many new versions for these languages. When these lose their relevance, new programming languages evolve and the cycle goes on.

Among the other programming languages, Node.JS is found to be scalable, easy to learn, provides high performance, and has a large community.

These are reasons enough for any developer having a client, who wants to scale his business in a progressive manner. However, during the addition of advanced features or simply during integration, numerous errors pop up. In such a situation, a project with Node.JS takes a lot of time for development.

nodejs-error-handling-system-img1

On the contrary, node.js error handling is crucial for your mobile app to run smoothly. If you are wondering about the solution when node.js throw custom error, you should follow js error handling best practices. Let’s understand about Node.js errors in detail.

What are the types of error in Node.js?

If you are a JavaScript or Node.JS developer, you would know the importance of launching a website or a mobile application without errors. In order to handle errors, it is important that we first understand what it is.

What is Operational Error?

Operational errors can be identified while running a software. These cannot be classified as bugs. In reality, operational errors can occur on a timely basis mostly under the influence of the external factors.

Some common scenarios that give rise to operational errors include a user making a decision to attempt an SQL injection through the insertion of SQL queries in the input field.

Below are some more instances when operational errors can occur:

  • Users request timeout
  • Fails to connect to database server
  • Users input invalid responses into the system
  • Resources looked up by users not found in the database
  • Fails to resolve a hostname
  • System lacks memory
  • Socket hangs up

What is Programmer Error?

In sharp contrast to operational errors, programmer errors are the actual errors in an application. In order to resolve these errors, developers can identify these errors and codes.

In most of the cases, programmer errors are common when a rookie developer sets to develop a mobile application. In other situations, while developing a high-level application.

In other scenarios, it is difficult to handle such types of errors as broken codes leads to these kinds of errors.

Below are some instances when programmer errors can occur:

  • Application tries to read the property of something ‘undefined’.
  • Pass an object wherein IP address was to be placed.
  • Pass a string wherein an object was required.
  • Calling an asynchronous function without using a callback.

nodejs-error-handling-system-cta1

How to Handle Errors in Node.JS?

Error handling in Node.JS is not the task of a rookie. It takes a high-level of experience to reach this stage. As a result, you should be aware about certain aspects related to Node.JS first.

You should know how Node.js architecture works, Node.JS frameworks as developers, and development practices meant for Node.JS developers to follow.

This will help you go in-depth with the exception handling in Node. Among the other development errors that developers face, node.js error response is not quite popular. It doesn’t get the required traction. This is the reason why not many developers can build a Node.JS error-handling system.

However, if you want to be among the top developers who want to improve your understanding of Node.js errors, we have got you covered. Below we discuss the bad error handling patterns and proper process of handling errors in Node.JS with examples:

Pattern 1: Wrong Usage of Callbacks

This type of error may arise when the code that you write is dependent on the external API that requires a callback to receive an outcome that it expects.

Let’s take an example to understand this:


    'use strict';
    const fs = require('fs');
    const write = function () {
        fs.mkdir('./writeFolder');
        fs.writeFile('./writeFolder/foobar.txt', 'Hello World');
    }
    write();

Such type of error in Node.JS can be classified as a system error. Till Node.JS 8 and programming level 8, the code was legal and developers would simply implement fire and forget commands. As a result, developers could do away without needing to have a callback to these function calls. In addition, error handling was not required.

However, in the example mentioned above, when the writingfolder has not been written, the writeFile command would not run. This would further led to the race condition as the second command would start even before the first command has not yet finished.

So, it is important that we resolve the race condition first. We would resolve this race condition by providing a callback to mkdir. This is to be done to ensure that this directory is created before the second command starts running.

So, the revised code would be like the following:


    'use strict';
const fs = require('fs');
const write = function () { fs.mkdir('./writingfolder', () => {; fs.writeFile('./writingfolder/writebar.txt', 'Hello World!'); }); }
write();

Though the race condition is resolved over here, the entire issue with the callback is not resolved yet. We still do not know whether the writingfolder is created or not. So, we will build an error handling system with callbacks.

How to Build an Error Handling System with Callbacks?

When you approach an error, it is crucial that you take the error-first route. This simply means that you should check whether an error has come from the running of the program before any other data.

In such a way, the right approach to error handling with callbacks is:


    'use strict';
    // Wrong
    const fs = require('fs');
    const write = function (callback) {
        fs.mkdir('./writeFolder', (err, data) => {;
            if (err) return callback(err)
            fs.writeFile('./writingfolder/writebar.txt', 'Hello World!');
        });
    }    
    write(console.log);

Pattern 2: Wrong Usage of Promises

Most of the developers think that promises are relatively more reliable than callbacks. This is because the developers think that some of the external APIs offer promising execution on the code that they are dependent upon.

The modern approach to Node.JS is to not use callbacks. Our developers leverage promises in Node.JS codebases.

So, you can reimplement a code with a promise in the following manner:


    'use strict';    
    const fs = require('fs').promises;
    const write = function () {
        return fs.mkdir('./writingfolder').then(() => {
            fs.writeFile('./writingfolder/writebar.txt', 'Hello world!')
        }).catch((err) => {
            // catch all important errors
            console.error(err)
        })
    }

Now is the time to analyse the code that we have written. Here you will find that when the code is branching off fs.mkdir promise to another promise before even handling the call.

A more effective way to go about the promise code is as mentioned below:


    'use strict';    
    const fs = require('fs').promises;
    const write = function () {
        return fs.mkdir('./writingfolder').then(() => {
            fs.writeFile('./writingfolder/writebar.txt', 'Hello world!')
                // do something
            }).catch((err) => {
                console.error(err)
            })
        }).catch((err) => {
            // catch all important errors
            console.error(err)
        })
    }

The above-mentioned code is not expected to scale as we have included numerous promise chains to call. In such a case, more errors would pop up instead of resolving it. This will fail the purpose of using the callback.

How to Build an Error Handling System Using a Promise?

The best way to go about it is to promise a callback-oriented API for better error-handling. However, it only sounds easy. In reality, if you attempt to do so, yet another maze will be created.

Let’s understand why:


    function doesWillNotAlwaysSettle(arg) {
        return new Promise((resolve, reject) => {
            doATask(foo, (err) => {
                if (err) {
                    return reject(err);
                }
                    if (arg === true) {
                        resolve('I am all Done')
                    }
                });
            });
        }

In the example mentioned above, if the arg is false, there is no error left to call the doATask function. The promise will just be there in the form of a memory leak.

Since the promise can only be in one state—either resolved or pending, dead zones in promises are acceptable.

Let’s check it with the help of an example:


    function deadZonePromise(arg) {
        return new Promise((resolve, reject) => {
            doATask(foo, (err) => {
                resolve('I’m all Done');
                throw new Error('I am never reached') // Dead Zone
            });
        });
    }

From the above code, you can find that when the promise is resolved, the subsequent line turns into a dead zone. As a result, any synchronous error-handling will never be shown and it will get resolved here itself.

These were two types of crucial error handling system. However, there are multiple real-life scenarios that give rise to the errors. They are as mentioned below:

How to Transform Error into String?

In most of the cases, the errors that are returned from the execution of a program does not seem good enough. As a result, developers choose to add a personalized message.


    'use strict';
    function readTemplate() {
        return new Promise(() => {
            databaseGet('query', function(err, data) {
                if (err) {
                    reject('Template not found. Error: ', + err);
                } else {
                    resolve(data);
                }
            });
        });
    }
    readTemplate();

Here, the developer is attempting to improve the message that is sent to the user when an error pops up by databaseGet. This approach used by developers has many shortcomings.

One of the threats is that the developer loses any additional information that can be returned along with the error.To have a better shot at this error, developers should let the error be as it is.

How to Ignore the Error Completely?

Such a scenario may arise when an end user is trying to log in to your portal and an error pop up. In such a situation, you want to catch the error and display a personalized message. However, in the attempt, you may completely ignore the error before sending it to the log.

Let’s consider an example:


    router.get('/:id', function (req, res, next) {
        database.getData(req.params.userId)
        .then(function (data) {
            if (data.length) {
                res.status(200).json(data);
            } else {
                res.status(404).end();
            }
        })
        .catch(() => {
            log.error('db.rest/get: could not get data: ', req.params.userId);
            res.status(500).json({error: 'Internal server error'});
        })
    });

In the example mentioned above, you can find that the error that has gone to the user might display 500 when the call made to the database fails. However, when the same code is run in reality, the status code that will be displayed will be of 400.

When such an error would surface, even the seasoned developer would not know what actually went wrong in the code. This is because the 500 error would be thrown when there is any issue with the internal server. So, it is okay to leave the error without adding any customized message for users.

How to not Accept the Error Thrown?

Let’s take a look at another scenario. There is an error thrown from an API. However, the developer does not accept it. In addition, the developer converts the error in forms that turns it redundant for debugging.

Let’s understand this scenario using a code:


    async function doThings(input) {
        validate(input);
        try {
            await db.create(input);
        } catch (error) {
            try {
                await rollback();
            } catch (error) {
                logger.log('Rollback failed', error, 'input:', input);
            }
            throw error;
        }
    }

In the above-mentioned case, just a single try/catch block is used and any error produced is logged in. Also, the original message can be displayed without losing any error. In case you want to do a robust test, you should look for errors as well as edge cases.

What are the Best Practices to Handle Node.JS Errors?

Usually, only the top-caller may know the appropriate response is when the application crashes. However, directing and reporting all errors to a top caller is not the solution, as the callback might not know the source of the error. So, for any type of error, there are certain best practices that you can follow:

Deal with Errors

When you encounter any type of failure, the best possible way to counter it is to deal with it directly. For instance, if you encounter an ENOENT error, you might have accessed the file for the first time. In such a situation, you would have to create a log file first.

Another common error may be related to the database connection. The connection to the server may be bad, which might lead to socket hang-up error. In such a situation, you need to reconnect the server. Such small instances might pop up, and it is up to you to deal with it directly.

nodejs-error-handling-system-cta2

Communicate the Failure to your Client

If you are new to development, you might not know how to deal with these errors. In such a situation, it is recommended that you do not mess up with the coding further more. Just abort the system and communicate about the error with the client.

Here, you can either seek developmental assistance from other developers. If you cannot modify the error, pass the same to the client. He can hire our experienced Node.JS experts to resolve these errors.

Retry the Operation

One of the most common errors is 503. In such a case you may encounter it a lot of time and your developers may not know what exactly to do with this. Since we have encountered such errors, our developers reboot the operation.

However, when you retry the operation, it is crucial that you document that your client needs to reboot it multiple times. On the contrary, numerous times there are some of the other errors and you should not assume that the operations need a reboot. Check what error is popping up before going for a reboot.

Log the Error

Every programming language has some or the other issues. In such a case, it is crucial for you to understand these limitations. Since these are in-built errors, you cannot actually do anything about it. So, the best possible way is to log the error in the system and let the community help you out.

While you encounter such a system error, it is crucial for you to not reboot your system. This may lead to a crash, and you may lose your entire development.

Use Built-in Error Object

Some errors are thrown in the form of a string or a custom type. This, in turn, complicates the error-handling system and leads to interoperability amidst various modules. In such a case, if you disapprove of a promise, emit an error, or throw an exception, you should include a built-in error object.

Inclusion of a built-in error object can help increase uniformity in the system and reduce the loss of information.

Draw a Line Between the Programmer Error and Operational Error

As discussed earlier, operational errors are types of errors that can be handled. On the contrary, programmer errors are a bit tough to identify. Usually, the programmer’s error prompts to restart the system.

In such a case, when you are able to differentiate between the programmer error and operational error, you can easily debug the system.

The Final Words

It is important to take the right approach to handle Node.JS errors in your mobile application so that you can pass on a seamless system to your client. Create a secure REST API in Node.js to easily get your hands on any protocol.

Custom error handling in Node.JS can turn extremely tricky if you do not know the source of errors. Hence, you should check for the response that the system has been generating first.

Since Node.JS is a popular technology used by the top mobile applications, it is not going to be redundant very soon. In such a case, you cannot do away without understanding the global error handling in node.js.

Either you can learn about it or you can hire dedicated Node.JS developers from Peerbits to help you resolve your Node.JS errors.

Frequently asked questions about Node.JS Error-handling

Here are some of the questions that most of the Node.js developers have in their mind while exception handling in Node.

It is important to build a proper error-handling system as it turns an app robust that leads to impressive user experience and high productivity.

Some of the strategies that you can use for error-handling includes using proper callbacks, promises, handling centralized error-handling, and uncaught exceptions.

Error-handling is crucial as it prevents any app from turning error-prone and helps save the time taken for the development. This, in turn, helps clients to develop mobile applications and launch it at better market times.

Node.JS is a good technology if used properly. It depends on the expertise of developers. For instance, Peerbits has the best Node.JS developers for you to handle Node.js errors efficiently.

nodejs-error-handling-system-cta3