Aerodynamika
Aerodynamika

Reputation: 8413

How to handle errors in Express 4 from within the Node.Js app?

I upgraded to Express 4 and have the following problem with error handling.

Before I used to have the code in app.js — after all the possible routes I had

var routes = require('./routes')
app.use(routes.notfound)
app.use(routes.error)
app.use(routes.badrequest)

And then inside the /routes/index.js I had:

exports.notfound = function(req, res) {
    res.status(404).format({
        html: function() {
            res.render('404')
        },
        json: function() {
            res.send({
                message: 'We did not find what you were looking for :(',
            })
        },
        xml: function() {
            res.write('<error>\n')
            res.write(
                ' <message>We did not find what you were looking for :(</message>\n'
            )
            res.end('</error>\n')
        },
        text: function() {
            res.send('We did not find what you were looking for :(\n')
        },
    })
}

Now when I call for 404 elsewhere in the app (not in app.js) using res.send(404) I get the right 404 code response but I don't get to the part where it selects whether it shows html or json or text.

How do I do that?

Upvotes: 0

Views: 150

Answers (1)

DaCurse
DaCurse

Reputation: 825

You need to handle error catching differently, here is one way to do so:

Create a middleware after all of your routes that will catch errors you pass to it, the callback would take in an extra parameter containing details about the error:

app.use((err, req, res, next) => {
  // Handle the error here
});

Whenever you want to render an error, you can use next in your routes to pass it to this middleware, and pass extra information you can use to decide how to handle the error. There is a module called http-errors that can create objects like that for you. Here is an example route:

const createError = require('http-errors');

app.get('/posts', (req, res, next) => {
  // Logic...
  if(/* some condition */) {
    next(createError(404));
  }
});

This will pass the error object created to your error handling middleware, and from there you can choose how to handle it.

To extend this, and to make it work better with asynchronous code, you can wrap your router's callbacks with a function that will make sure exceptions that get thrown are passed over to the error handling middleware, this comes in handy when working with async and await:

// Wrapper function to forward errors from async scopes
const wrap = fn => (...args) => fn(...args).catch(args[2]);

app.get('/posts', wrap(async (req, res) => {
  // Logic...
  await controller.get('posts'); // Promise rejections will get forwarded to middleware
}));

This also lets you just throw the error object instead of calling next.

Upvotes: 0

Related Questions