DeployU
Interviews / Backend Engineering / How do you handle errors in asynchronous Node.js code?

How do you handle errors in asynchronous Node.js code?

practical Error Handling Interactive Quiz Code Examples

The Scenario

You are a backend engineer at an e-commerce company. You are writing a new service that makes several asynchronous calls to other services to process an order.

You need to make sure that you handle errors correctly at each step of the process. If any of the asynchronous calls fail, you need to be able to catch the error and respond to the user with an appropriate error message.

The Challenge

Explain how you would handle errors in asynchronous Node.js code. What are the different error handling patterns that you would use for callbacks, promises, and async/await?

Wrong Approach

A junior engineer might not have a clear strategy for handling errors in asynchronous code. They might just wrap their code in a `try...catch` block, which will not work for all asynchronous patterns. They might also not be aware of the importance of using a centralized error handling middleware.

Right Approach

A senior engineer would have a deep understanding of the different error handling patterns for asynchronous code. They would be able to explain how to use error-first callbacks, `.catch()` for promises, and `try...catch` for async/await. They would also know how to use a centralized error handling middleware to handle all errors in a consistent way.

Step 1: Choose the Right Pattern for Each Asynchronous Approach

ApproachError Handling Pattern
CallbacksError-first callbacks: the first argument to the callback function is always the error.
PromisesThe .catch() method is used to handle errors.
Async/awaitThe try...catch block is used to handle errors.

Step 2: Code Examples

Here are some code examples that show how to handle errors in each of the three approaches:

Callbacks:

const fs = require('fs');

fs.readFile('file1.txt', 'utf8', (err, data) => {
  if (err) {
    console.error(err);
    return;
  }
  // ...
});

Promises:

const fs = require('fs').promises;

fs.readFile('file1.txt', 'utf8')
  .then(data => {
    // ...
  })
  .catch(err => {
    console.error(err);
  });

Async/await:

const fs = require('fs').promises;

async function readFile() {
  try {
    const data = await fs.readFile('file1.txt', 'utf8');
    // ...
  } catch (err) {
    console.error(err);
  }
}

Step 3: Use a Centralized Error Handling Middleware

In a real-world application, you would not want to handle errors in each individual route handler. Instead, you would use a centralized error handling middleware to handle all errors in a consistent way.

In an Express.js application, you can define an error handling middleware like this:

app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

This middleware will be called whenever an error occurs in your application.

Practice Question

You are using `async/await` and you want to handle errors from multiple `await` calls in a single place. Which of the following would you use?