Some error handling techniques in Node.js

Some error handling techniques in Node.js

Daily short news for you
  • For over a week now, I haven't posted anything, not because I have nothing to write about, but because I'm looking for ways to distribute more valuable content in this rapidly exploding AI era.

    As I shared earlier this year, the number of visitors to my blog is gradually declining. When I looked at the statistics, the number of users in the first six months of 2025 has dropped by 30% compared to the same period last year, and by 15% compared to the last six months of 2024. This indicates a reality that users are gradually leaving. What is the reason for this?

    I think the biggest reason is that user habits have changed. They primarily discover the blog through search engines, with Google being the largest. Almost half of the users return to the blog without going through the search step. This is a positive signal, but it's still not enough to increase the number of new users. Not to mention that now, Google has launched the AI Search Labs feature, which means AI displays summarized content when users search, further reducing the likelihood of users accessing the website. Interestingly, when Search Labs was introduced, English articles have taken over the rankings for the most accessed content.

    My articles are usually very long, sometimes reaching up to 2000 words. Writing such an article takes a lot of time. It's normal for many articles to go unread. I know and accept this because not everyone encounters the issues being discussed. For me, writing is a way to cultivate patience and thoughtfulness. Being able to help someone through my writing is a wonderful thing.

    Therefore, I am thinking of focusing on shorter and medium-length content to be able to write more. Long content will only be used when I want to write in detail or delve deeply into a particular topic. So, I am looking for ways to redesign the blog. Everyone, please stay tuned! 😄

    » Read more
  • CloudFlare has introduced the pay per crawl feature to charge for each time AI "crawls" data from your website. What does that mean 🤔?

    The purpose of SEO is to help search engines see the website. When users search for relevant content, your website appears in the search results. This is almost a win-win situation where Google helps more people discover your site, and in return, Google gets more users.

    Now, the game with AI Agents is different. AI Agents have to actively seek out information sources and conveniently "crawl" your data, then mix it up or do something with it that we can't even know. So this is almost a game that benefits only one side 🤔!?

    CloudFlare's move is to make AI Agents pay for each time they retrieve data from your website. If they don’t pay, then I won’t let them read my data. Something like that. Let’s wait a bit longer and see 🤓.

    » Read more
  • Continuing to update on the lawsuit between the Deno group and Oracle over the name JavaScript: It seems that Deno is at a disadvantage as the court has dismissed the Deno group's complaint. However, in August, they (Oracle) must be held accountable for each reason, acknowledging or denying the allegations presented by the Deno group in the lawsuit.

    JavaScript™ Trademark Update

    » Read more

The problem

Errors are a constant presence in application development. There's a saying that as long as there is code, there will always be "bugs". There are obvious errors that we can anticipate, and then there are those unknown errors that can catch us by surprise.

Errors can cause inconvenience and sometimes even have serious consequences. Therefore, error handling is always an important aspect of programming. In this article, I will present some "elegant" error handling techniques in the Node.js environment.

Techniques

Use async/await or promises to handle errors in asynchronous functions

Avoid using callbacks that can result in callback hell, making your code deeply nested and hard to read and maintain.

Here's an example of using callbacks to handle errors:

getData(someParameter, function(err_a, a) {
    if(err_a!== null) {
        getMoreDataA(a, function(err_b, b) {
            if(err_b !== null) {
                getMoreDataB(b, function(err_c, c) {
                    getMoreDataC(c, function(err_d, d) {
                        if(err_d !== null ) {
                            // do something
                        }
                    })
                });
            }
        });
    }
});

Instead, use promises to handle errors in a more "elegant" way:

return getData(someParameter)
    .then(getMoreDataA)
    .then(getMoreDataB)
    ...  
    .catch(err => handle(err));

However, promises can make debugging difficult. Use the "graceful" syntax of async/await combined with try/catch:

try {
    const a = await getData(someParameter);
    const b = await getData(someParameter);
} catch(err) {
    handle(err);
}

Throw an Error object instead of any other object when reporting an error

JavaScript allows us to "throw" any object to report an error, whether it's a number, a string, or an object. However, it's best to throw an Error object to ensure consistency in your code and with libraries. Furthermore, an Error object retains important information such as the StackTrace, which indicates where the error occurred.

// don't do this
if (!condition) {
    throw ('invalid condition');
}

// do this instead
if (!condition) {
    throw new Error('invalid condition');
}

// or you can even do better by creating a custom error object
// that carries more useful information
function MyError(name, description, ...args) {
    Error.call(this);
    Error.captureStackTrace(this);
    this.name = name;
    this.description = description;
    ...  
};

MyError.prototype = Object.create(Error.prototype);
MyError.prototype.constructor = MyError;

if (!condition) {
    throw new MyError('CONDITION_NOT_VALID', 'invalid condition');
}

Centralize error handling

Create one or more functions that are ready to receive an error object and then analyze or distribute the error to different places. Avoid handling errors separately, as this can make your code harder to control.

Imagine what you would do when an error occurs. Do you log it to the console, send it to monitoring or logging services, write it to a file, check conditions and classify the error... there are many things to do with an error once it is thrown. Therefore, gather them in one or more specialized error handling functions.

Beware of unhandledRejection errors

This is a very annoying error in Node.js that can cause your application to hang and no longer process any requests. This error occurs when you mishandle a promise, specifically when there is no error handling function for reject in the promise.

UserModel.findByPk(1).then((user) => {
  if(!user)
      throw new Error('user not found');
});

To catch this error, we need to use process.on.

process.on('unhandledRejection', (reason, p) => {
  throw reason;
});

process.on('uncaughtException', (error) => {
  errorManagement.handler.handleError(error);
  if (!errorManagement.handler.isTrustedError(error))
    process.exit(1);
});

process.on is usually placed in the index.js file, which is the first file that your application starts with, so that it can "listen" to any signals coming from mishandling promises. This allows you to handle the error properly and prevent the server from hanging.

Never trust input data, always validate

We provide POST, PUT... endpoints to allow clients to send data to our application, but the data cannot always be trusted. Therefore, the safest approach is to always validate the received data to prevent unintentional or malicious submission of incorrect data, which can cause errors or even system breaches.

There are many excellent libraries that can help with this, such as Joi or ajv.

const schema = Joi.object({
  username: Joi.string()
    .alphanum()
    .min(3)
    .max(30)
    .required(),  

  password: Joi.string()
    .pattern(new RegExp('^[a-zA-Z0-9]{3,30}$')),  
});

let body = { username: '2coffee', password: '2coffee' };
schema.validate(body); // -> { value: { username: '2coffee', password: '2coffee' } }

let body = {};
schema.validate(body); // -> { value: {}, error: '"username" is required' }

Use a professional logging system

Logging is a way to track and monitor errors that have occurred in the past, allowing you to retrieve them at any time. I have a separate article on logging in Node.js applications, which you can read at Logging in Node.js Applications at 3 Different Levels.

Write unit tests

Unit tests are one of the methods that help you catch errors early in the development process. While writing unit tests can be time-consuming, it is worth investing your time in them. Once your unit tests are working well, they will save you a lot of time and cost in the long run.

"Exit" the application when necessary

In cases where you encounter specific errors for which there is no immediate solution or recoverable state, the temporary solution is to crash the application and rely on DevOps tools to restart it. This may not be the best approach, but restarting your application will restore it to the state before the error occurred.

Use APM (Application Performance Management)

Application Performance Management (APM) is used to monitor and manage the performance and availability of an application. APM tries to detect and diagnose performance issues and promptly notify you when there's a problem.

There are many APM products and services in the market, ranging from open-source to paid solutions. The features vary from simple to complex, from basic monitoring to deep integration for monitoring complex systems.

Start with a simple APM service like uptimerobot.com. It monitors the uptime of your application by sending a request every 5 minutes and waiting for a successful or failed response.

uptimerobot.com

New Relic is a more comprehensive tool that monitors various aspects such as detailed error stack traces, response times, performance bottlenecks, and detailed statistics.

New Relic

Conclusion

These are some error handling techniques that I recommend for Node.js applications. There may be other techniques that I'm not aware of, so if you discover any missing techniques, please leave a comment below.

Premium
Hello

5 profound lessons

Every product comes with stories. The success of others is an inspiration for many to follow. 5 lessons learned have changed me forever. How about you? Click now!

Every product comes with stories. The success of others is an inspiration for many to follow. 5 lessons learned have changed me forever. How about you? Click now!

View all

Subscribe to receive new article notifications

or
* The summary newsletter is sent every 1-2 weeks, cancel anytime.

Comments (1)

Leave a comment...
Avatar
Jess Vanes2 years ago

Hướng dẫn dùng mấy tool apm đi ạ

Reply
Avatar
Xuân Hoài Tống2 years ago

@gif [ISOckXUybVfQ4] Chắc là sẽ hơi lâu ạ