joepie91's Ramblings

home RSS

Using Promises (eg. bluebird) with Express

14 May 2015

When using a promises library like bluebird in Express, you might have found it somewhat awkward to use them correctly - it's easy to forget to tack on a .catch statement at the end of a chain of promises.

Fortunately, there's a solution for that, and it's called express-promise-router!

Using express-promise-router

express-promise-router is a slightly modified version of the regular router that ships with Express, that expects a route to return a promise object. It then automatically attaches an error handler to the promise, that will forward any errors to your regular error handling middleware, so that you can handle them as if you called next(err).

With the regular Express router, your code might look something like this:

var express = require("express");
var router = express.Router();

router.get("/", function(req, res, next){
    Promise.try(function(){
        return doSomeAsyncThing();
    }).then(function(value){
        return doAnotherAsyncThing(value);
    }).then(function(newValue){
        res.send("Done!");
    }).catch(function(err){
        next(err);
    });
});

module.exports = router;

And this is what it looks like with express-promise-router:

var expressPromiseRouter = require("express-promise-router");
var router = expressPromiseRouter();

router.get("/", function(req, res){
    return Promise.try(function(){
        return doSomeAsyncThing();
    }).then(function(value){
        return doAnotherAsyncThing(value);
    }).then(function(newValue){
        res.send("Done!");
    });
});

module.exports = router;

Note the following:

  1. We no longer need to define a .catch handler.
  2. We now return the chain of promises from the route.

That's it! All errors in your chain of promises now end up in your regular error handling middleware, where you can log them and send an error page.

Does this support the same features as a regular Express router?

Yep. The express-promise-router module really just wraps the native router, and adds error handling to the route methods. That's all it does.

Can I also return promises from .param() calls?

Yep, since 0.0.7. They're handled in the same way as the regular route methods. If an error occurs in the .param() call, the request will be aborted, and the route after it won't be run - in other words, like it should be :)

And how about .use() and .all()?

Yes, same as for .param().

What if I don't return a chain of promises from my route? Will it break?

Nope. The express-promise-router module only implements special behaviour when a chain of promises is returned. Otherwise, it has the default behaviour of the Express router.

Can I still use next() manually?

Yes.

My rejections are ending up on my console anyway. What gives?

Make sure you're actually returning the chain of promises from your route or middleware. It can't handle promises that you don't give to it!

How do I send different kinds of errors? I want to send different HTTP status codes.

Ideally, you'd use a module like errors for this. You can either use the standard HTTP error methods that it provides, or even create your own errors - and then handle all of them in your error handling middleware, like normal.

You can just do something along these lines:

app.use(function(err, req, res, next)
    if(err.status != null) {
        var statusCode = err.status;
    } else {
        var statusCode = 500;
    }

    res.status(statusCode).send(err.message);
}

My next Node.js article will be an in-depth guide to promises, and how they can help you escape from callback hell. I know I promised that last time, but this time it's really going to be the next post :)

I am currently offering Node.js code review and tutoring services, at affordable rates. More details can be found here.