DEV Community

folacode22
folacode22

Posted on • Updated on

Handling exception in NodeJS Express

hero

Debugging errors is the hardest part of programming. Errors can appear in your code in a variety of ways, whether as syntax errors, errors in logic, or the most dreaded of all, runtime errors. Runtime errors occur whenever something unexpected occurs in your application, and they often lead to catastrophic issues that can crash your program.

Like many languages, Node.js provides a mechanism to anticipate errors before they occur. When an error occurs in your code, it turns into an object called an exception. Properly handling these exceptions allows you to recover gracefully from unforeseen issues, resulting in a much better user experience.

Table of contents

  • Creating exceptions
  • Error objects
  • Handling exceptions
  • Catching uncaught exceptions
  • Exceptions with promises
  • error handling in synchronous
  • error handling in express

Creating exceptions

An exception is created using the throw keyword:

throw

As soon as JavaScript executes this line, the normal program flow is halted and the control is held back to the nearest exception handler.

Usually in client-side code value can be any JavaScript value including a string, a number or an object.

In Node.js, we don't throw strings, we just throw Error objects.

Error objects

An error object is an object that is either an instance of the Error object, or extends the Error class, provided in the Error core module:

throw 1

Handling exceptions

An exception handler is a try/catch statement.

Any exception raised in the lines of code included in the try block is handled in the corresponding catch block:

try {
  // lines of code
} catch (e) {}

Enter fullscreen mode Exit fullscreen mode

e in this example is the exception value.

You can add multiple handlers, that can catch different kinds of errors.

Exceptions with promises

Using promises you can chain different operations, and handle errors at the end:


performFunction1()
  .then(performFunction2)
  .then(performFunction3)
  .catch(err => console.error(err));

Enter fullscreen mode Exit fullscreen mode

How do you know where the error occurred? You don't really know, but you can handle errors in each of the functions you call (performfunction(x)), and inside the error handler throw a new error, that's going to call the outside catch handler:

const performFunction1 = () => {
  // ...
  try {
    // ...
  } catch (err) {
    // ... handle it locally
    throw new Error(err.message);
  }
  // ...
};
Enter fullscreen mode Exit fullscreen mode

To be able to handle errors locally without handling them in the function we call, we can break the chain. You can create a function in each then() and process the exception:

performFunction1()
  .then(() => {
    return performFunction2().catch(err => {
      // handle error
      throw err; // break the chain!
    });
  })
  .then(() => {
    return performFunction3().catch(err => {
      // handle error
      throw err; // break the chain!
    });
  })
  .catch(err => console.error(err));

Enter fullscreen mode Exit fullscreen mode

Error Handling in express

Error Handling refers to how Express catches and processes errors that occur both synchronously and asynchronously. Express comes with a default error handler so you don’t need to write your own to get started.

It’s important to ensure that Express catches all errors that occur while running route handlers and middleware.

Errors that occur in synchronous code inside route handlers and middleware require no extra work. If synchronous code throws an error, then Express will catch and process it. For example:

app.get('/', (req, res) => {
  throw new Error('BROKEN') // Express will catch this on its own.
})
Enter fullscreen mode Exit fullscreen mode

For errors returned from asynchronous functions invoked by route handlers and middleware, you must pass them to the next() function, where Express will catch and process them. For example:

app.get('/', (req, res, next) => {
  fs.readFile('/file-does-not-exist', (err, data) => {
    if (err) {
      next(err) // Pass errors to Express.
    } else {
      res.send(data)
    }
  })
})
Enter fullscreen mode Exit fullscreen mode

error in asynchronous code, route handlers and middleware that return a Promise will call next(value) automatically when they reject or throw an error. For example:

app.get('/user/:id', async (req, res, next) => {
try{
  const user = await getUserById(req.params.id)
  res.send(user)}
catch(err){next(err)}
})
Enter fullscreen mode Exit fullscreen mode

Top comments (0)