Error Handling in Express

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-

filter_none

edit
close

play_arrow

link
brightness_4
code

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

chevron_right


 
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 –
    filter_none

    edit
    close

    play_arrow

    link
    brightness_4
    code

    app.get('/', function (req, res) {
        
        // Express catches this on its own
        throw new Error('Died')
     })

    chevron_right

    
    

  • 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
    filter_none

    edit
    close

    play_arrow

    link
    brightness_4
    code

    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)
        }
      })
    })

    chevron_right

    
    

  • Route handlers and middlewares that return a Promise will call next(value) automatically when they reject or throw an error.
    filter_none

    edit
    close

    play_arrow

    link
    brightness_4
    code

    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)
    })

    chevron_right

    
    

    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 –
    filter_none

    edit
    close

    play_arrow

    link
    brightness_4
    code

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

    chevron_right

    
    

    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 – 

    filter_none

    edit
    close

    play_arrow

    link
    brightness_4
    code

    app.get('/', function (req, res, next) {
      setTimeout(function () {
        try {
          throw new Error('Died')
        } catch (err) {
          next(err)
        }
      }, 100)
    })

    chevron_right

    
    

  • 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 –
    filter_none

    edit
    close

    play_arrow

    link
    brightness_4
    code

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

    chevron_right

    
    

    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:

    filter_none

    edit
    close

    play_arrow

    link
    brightness_4
    code

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

    chevron_right

    
    

    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 – 

    filter_none

    edit
    close

    play_arrow

    link
    brightness_4
    code

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

    chevron_right

    
    

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

    filter_none

    edit
    close

    play_arrow

    link
    brightness_4
    code

    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!')
    })

    chevron_right

    
    




    My Personal Notes arrow_drop_up

    Check out this Author's contributed articles.

    If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

    Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.


    Article Tags :

    1


    Please write to us at contribute@geeksforgeeks.org to report any issue with the above content.