Taking Toddler Steps with Node.js – Express Routing

January 20th, 2012

In the previous post I provided a short introduction to Express, a web development framework built on top of connect that is heavily inspired by Sinatra. For this post we’ll dive into a couple of styles for dealing with routes in Express.

Express simply uses HTTP verbs for its routing API.

// Index
app.get('/runs', function(request, response) { });            

// New run 
app.get('/runs/new', function(request, response) { });

// Create a new run
app.post('/runs', function(request, response) { });

// Show run
app.get('/runs/:id', function(request, response) { });

// Edit run
app.get('/runs/:id/edit', function(request, response) { });

// Update run
app.put('/runs/:id', function(request, response) { });

// Delete run
app.delete('/runs/:id', function(request, response) { });

Well, that’s pretty much all that you need to get started. The routes that we specified here are treated as plain old regular expressions. Note that in order to make the put and delete routes work, we have to add a hidden field to the view.

<input name="_method" value="PUT" type="hidden">

But the thing that I personally struggled with the most was finding out a decent way to divide up these routes into separate modules without too much of a hassle. Most sample and demo applications out there that use Express usually have all their routes specified in a single app.js file. This is something that I don’t like very much as this can become unmaintainable faster that you might think. Roughly 2000 years ago, there was this great emperor (and many after him) who valued the principle of Divide and Conquer. In order to create maintainable applications, being able to divide up these routes is quite essential. There are several ways to do this.

Express Resource

This library enables us provide resourceful routing. As usual, express-resource can be installed using npm by using the following command:

 npm install express-resource

Using express-resource, we can create controller modules and use them from our main module. The following snippet shows how a simple controller looks like:

exports.index = function(request, response){
    response.send('Index runs');
};

exports.new = function(request, response){
    response.send('New run');
};

exports.create = function(request, response){
    response.send('Create run');
};

exports.show = function(request, response){
    response.send('Show run ' + request.params.id);
};

exports.edit = function(request response){
    response.send('Edit run ' + request.params.id);
};

exports.update = function(request, response){
    response.send('Update run ' + request.params.id);
};

exports.destroy = function(request, response){
    response.send('Delete run ' + request.params.id);
};

Now in the main module (app.js) we just have to add the following code:

var resource = require('express-resource')

// ...

application.resource('runs', require('./routes/runs'));

That’s it! Now all these routes are hooked up and ready to use. Express-resource has a few other neat features as well. Check out this episode from Node Tuts to learn more.

Although this seems like a good solution to divide up routes into controller modules, somehow it doesn’t resonate with me. All routes for a particular resource need to exist in the same module which still feels a bit unwieldy to me. I want to have an even more granular approach.

Super-duper Require

What I like to do is to separate routes based on their context:

  • runs/index.js ( index route )
  • runs/new.js ( new and create routes )
  • runs/show.js ( show route )
  • runs/edit.js ( edit and update routes )
  • runs/delete.js ( delete route )

Wouldn’t it be cool if we could just “require” the runs directory and hook up all routes exported by all the modules that exist in this directory? Well, meet super_duper_require! While still using express-resource, we can now add all these routes like so:

    application.resource('runs', super_duper_require(module, './routes/runs/'));

    This is how the super_duper_require function looks like:

    _ = require('underscore');
    
    function super_duper_require(mod, path) {
        var mixin = {};
          fileSystem.readdirSync(path)
              .forEach(function(filename) {
                _.extend(mixin, mod.require(path + filename));
            });
    
          return mixin;
    };

We just use the magnificent underscore.js library here to hook things up. This is just one of the fancy ways to solve the granularity problem. If we don’t want to use the express-resource library, we can always accomplish the same thing by going “plain old school” style.

Plain Old School

This is how I currently set up routing with Express. We no longer need the express-resource library for setting up our routes, but we can still use the same granularity as shown earlier. We also need underscore.js again, just as in the previous example, in order to stitch things together.

    var routes = require('./routes');
    var routes.runs = _.extend(require('./routes/runs'), 
                               require('./routes/runs/new'),
                               require('./routes/runs/show'),
                               require('./routes/runs/edit'),
                               require('./routes/runs/delete'));
                               
    ...
    
    function bootstrapRoutes(application) {
        app.get('/runs', routes.runs.index);            
        app.get('/runs/new', routes.runs.new);
        app.post('/runs', routes.runs.create);
        app.get('/runs/:id', routes.runs.show);
        app.get('/runs/:id/edit', routes.runs.edit);
        app.put('/runs/:id', routes.runs.update);
        app.delete('/runs/:id', routes.runs.delete);
    }

Up until now I’m pretty happy with this approach. I would love to hear how others divide up their routes into several modules. So please let me know if there are other awesomely cool ways to deal with this. In the mean time, I hope this helps.

Until next time.

  • http://blogs.msdn.com/gblock Glenn Block

    Very useful post Jan. I’ve done few different approaches

    1. Put all my route handling code in separate modules and keep all my routes in a single routes file. There routes delegate to the appropriate handler functions.

    2. Same as above only putting the route logic within the same file as the handling code, meaning I still had separate modules but they contain the route hookup and the handling code. In the main app I just require the different route modules.

    I will definitely check out the approaches you have listed above. If nothing else I need to start messing with underscore :-)

    Glenn

    • Anonymous

      I tried something similar as well but they felt like overhead although they don’t differ that much from the approaches I mentioned. I started with a seperate routes module but moved this to a bootstrap module to combine with other startup code. This enables me to keep the main app.js file to only a couple of lines.

      Definitely have a look at underscore. It’s a real live(time)-saver :-)

  • http://www.charliecrystle.com Charlie Crystle

    thanks! 
    question: how do I reference a DELETE route from the page?  The hidden field example tripped me up. Ok, I was already tripped…

    • Anonymous

      Just as you would do with a PUT:

      form(method=’post’, action=’/runs/#{ run.id }’)
      input(name=’_method’, value=’DELETE’, type=’hidden’)

      Or you could solve this with an AJAX call.

      • http://www.charliecrystle.com Charlie Crystle

        thx. so Express sees the element with name=method and maps it to app.del?

        have ajax, but the click event is misbehaving (attached to class, never unbinds naturally)

      • http://www.charliecrystle.com Charlie Crystle

        ah…sorry–so this is a form? I was hoping there was a straight path without the extra overhead of a form. :)

        • Anonymous

          You can pull it of without a form by using AJAX. Hava a look at the Nodepad sample: 
          https://github.com/alexyoung/nodepad/blob/master/app.js (line 318) and 
          https://github.com/alexyoung/nodepad/blob/master/public/javascripts/documents.js (line 206) This example uses Backbone though.

          • http://www.charliecrystle.com Charlie Crystle

            yeah–I’m currently using jQuery ajax, but there’s a messy problem of click events building up, I think because I’m attaching the click to a class (it’s a list of items, each can be deleted). 

            so

            $(‘.myClass’).click(function(e){   e.preventDefault();   deleteThing(this.id)
            })but because it’s attached to the class, I guess, a single click triggers an event for all on the list, resulting in multiple Ajax calls. The only way I’ve prevented it is from unbinding and binding again: $(‘.myClass’).unbind(‘click’).bind(‘click’, function…Which is ugly. So, form action it is!

          • Anonymous

            I think what you’re describing here is event bubbling. You can handle this by calling e.stopPropagation() right after e.preventDefault(). That should do the trick in that case.

            http://api.jquery.com/event.stopPropagation/ 

  • Muhammad Shuban

    There were many hurdles in learning Express Routing.

  • brain dumps

    that’s much clear now.
    http://www.realexams.me/

  • Artem Vovsya

    Ok, this this is my method to divide routes into modules(controllers):

    1.controllers.js

    var fs = require(‘fs’);
    module.exports = function(app, service){
    fs.readdir(__dirname + ‘/controllers’, function(err, files){
    if (err) throw err;
    files.forEach(function(file){
    var name = file.replace(‘.js’, ”);
    require(‘./controllers/’ + name)(app, service);
    });
    });
    };

    2. app.js

    require(‘./controllers.js’)(app, service, environment);

    3. And any js file in controller folder(for example home.js):

    module.exports = function (app, service) {
    var accountMiddleware = service.useModule(‘middleware/account’);
    app.get(‘/’, accountMiddleware.requireRole(‘user’), function(req, res){
    res.render(‘index’, { title: “Index” });
    });
    };

    If you interesting of this look at my template for Express – github.com/AzzNomad/express-template

    • Anonymous

      That looks very nice! Thx for sharing.