Skip to content
Related Articles

Related Articles

Improve Article

Error Handling in Express

  • Difficulty Level : Easy
  • Last Updated : 03 Jun, 2020
Geek Week

Error handling in Express is referred to as something that handles or processes errors that may come while executing any synchronous code or asynchronous code.

What do we mean by synchronous or asynchronous code?
A lot of times, it happens that an operation begins executing but for some reason faces some delay before completion. The common examples of such operations are HTTP requests such as AJAX requests, functions such as setTimeout etc. These operations begin, and then end when the response returns or when the timer ends. While the computer waits for these operations to complete, it moves on busying itself with the next lines of code. It keeps busy, but this causes a significant challenge — any code dependent on previous asynchronous code might be run before that asynchronous code is complete, meaning errors. Have a look below-




var data = makeAsyncRequest();
  
// Data is undefined
console.log("Data is " + data);

 
We can say that, when we execute something synchronously, we wait for it to finish before moving on another task. When we execute something asynchronously, we can move on to another task before it finishes. The above problem is solved using callbacks, middleware modules or by using the modern promise and await.

Catching Errors in Express

  • If synchronous code having route handlers and middleware throws any error, then without any effort and extra work, Express solves it by catching and processing the error without requiring any of our consent. Have a look at the below code –




    app.get('/', function (req, res) {
        
        // Express catches this on its own
        throw new Error('Died')
     })
  • If a route handler and middleware invokes asynchronous function which in turn produces some errors, then we have to explicitly pass the error to the next() function, where Express will catch and process them. The following illustration will help you to understand




    app.get('/', function (req, res, next) {
      fs.readFile('/file-is-not-available'
            function (err, data) {
        if (err) {
      
          // Passing errors to 
          // Express explicitly
          next(err) 
        } else {
          res.send(data)
        }
      })
    })
  • Route handlers and middlewares that return a Promise will call next(value) automatically when they reject or throw an error.




    app.get('/user/:id', async function (req, res, next) { 
      
        // Async keyword tells that it is
        // an asynchronous function
        var user = await getUserById(req.params.id)    
        res.send(user)
    })

    The await keyword can be used to indicate that the function that follows will be returning a promise, which should be awaited before executing any other dependent code. ‘await’ can only be used inside an async function.
    Next (next) will be called with either the thrown error or the rejected value in case if getUserById throws an error or rejects. If no rejected value is provided, next will be called with a default Error object provided by the Express router. If we pass anything to the next() function (except the string ‘route’), Express regards the current request as being an error and will skip any remaining non-error handling routing and middleware functions.

  • If a given callback in a sequence provides no data and only errors, then the code can be simplified as –




    app.get('/', [
      function (req, res, next) {
        fs.writeFile('/path-cannot-be-accessed',
                'data', next)
      },
      function (req, res) {
        res.send('OK')
      }
    ])

    In the above code, next is provided as the callback which runs without caring whether errors comes out or not. If there is no error, then the second handler also runs otherwise express just catches and processes the error.



    Now, look at the example below – 




    app.get('/', function (req, res, next) {
      setTimeout(function () {
        try {
          throw new Error('Died')
        } catch (err) {
          next(err)
        }
      }, 100)
    })
  • As we know, if a route handler and middleware invokes an asynchronous function which in turn produces some errors, then we have to explicitly pass the error to the next() function, where Express will catch and process them. However, in the above code, the error is not the part of the synchronous code, so we can’t simply pass it to the next function. We need to first throw the errors, catch those errors generated by asynchronous code, and then pass it to the Express. For this, we need to use the try..catch block to catch them. If you don’t want to use try and catch, then simply use promises as shown below –




    app.get('/', function (req, res, next) {
      Promise.resolve().then(function () {
        throw new Error('Died')
      
      // Errors will be passed to Express
      }).catch(next)
    })

    Since promises automatically catch both synchronous errors and rejected promises, you can simply provide next as the final catch handler and Express will catch errors, because the catch handler is given the error as the first argument.

  • Default Error Handlers: The default error handler catches the error when we call next and don’t handle it with a custom error handler. If we want to send a different response to the default, we have to write our own error handler. This default error-handling middleware function is added at the end of the middleware function stack. If you pass an error to next() and you do not handle it in a custom error handler, it will be handled by the built-in error handler, the error will be written to the client with the stack trace. The stack trace is not included in the production environment.

    When an error is written, the following information is added automatically to the response:

    • The res.statusCode is set from err.status (or err.statusCode). If this value is outside the 4xx or 5xx range, it will be set to 500.
    • The res.statusMessage is set as per the status code.
    • The body will be the HTML of the status code message when in production environment, otherwise will be err.stack.
    • Any headers specified in an err.headers object.

    If you call next() with an error after you have started writing the response (for example, if you encounter an error while streaming the response to the client) the Express default error handler closes the connection and fails the request.

    So when you add a custom error handler, you must delegate to the default Express error handler, when the headers have already been sent to the client:




    function errorHandler (err, req, res, next) {
      if (res.headersSent) {
        return next(err)
      }
      res.status(500)
      res.render('error', { error: err })
    }

    Note that the default error handler can get triggered if you call next() with an error in your code more than once, even if custom error handling middleware is in place.

    How to write Error handlers?

    The way we declare the middleware functions, in the same way, error handling functions are defined. However, error-handling functions have four arguments instead of three: (err, req, res, next). For example – 




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

    We need to define error-handling middleware last, after other app.use() and routes calls. The example is shown below – 




    app.get('/', (req, res, next) => {
     req.foo = true;
      setTimeout(() => {
        try {
          throw new Error('error');
        }
        catch (ex) {
          next(ex);
        }
      })
    });
    app.use((err, req, res, next) => {
      if (req.foo) {
        res.status(500).send('Fail!');
      }
      else {
        next(err);
      }
    })
    app.use((err, req, res, next) => {
      res.status(500).send('Error!')
    })



    My Personal Notes arrow_drop_up
Recommended Articles
Page :