The holistic view

The one area that is consistently lacking in node web application code is the organisation of internal dependencies.

I read a lot of code. Code reviews, research to find solutions to bugs and (sadly all to often) to simply understand a project that has confusing or incorrect documentation. Since moving my web application stack to Node I've spent more time than ever reading other developer's code.

Node applications, by virture of its require system, take a very modular and minimalist approach. The community likes to focus on many small, singular-focus, modules that are well tested (ahem). Applications join them together to create a Megazord. That’s great. The problem is the how of joining is not very well directed by popular frameworks.

I use express as the base for my web projects. Over the last few years I've often dug into web application projcets to see how they're structured. With that research, and a few live applications under my belt, I've found where the most common patterns are lacking as well as some helpful solutions.

From the bottom to the top

At the core of every express app you have your application instance. This object is used to add route listeners, configure template modules and listen for requests.

// index.js
var express = require('express');  
var app = express();  
app.listen(3000);  

This code is found within the index.js of nearly every single express application I've come across. Next to this is where they diverge: the configuration of your application's routes which are called to generate the responses to user requests. Adding a route requires access to the app instance. Route configuration is done by calling HTTP verbs (e.g. get and post) with a URL expression (e.g. /page/:slug) and a handler method. The two most common approaches I see follow on from eachother:

The "Don't worry about it" pattern

This "pattern" has you place all routes within an index.js file. There's a good chance that all the controller logic is there too.

// index.js
var express = require('express');  
var app = express();  
app.get('/', function (req, res) {  
    res.send('Hello world!');
});
app.listen(3000);  

There is no seperation of routes from the app, nor controllers from the routes. It works and it's easy to see everything when small in scope, but this file will get massive as the application scales and there is no clear organization of routes for developers to work with. Debugging is a nightmare, and testing is next to impossible. Luckily most applications do better, without much work.

The "My First Rodeo" pattern

We know that nodejs is all about modules so it's often that applications create a seperate route file (i.e. module) which is passed an application object to generate route listeners. Taken one step further each controller (or route method) is placed into it's own own file to keep our project structured and easier to read, test and maintain.

// index.js
var express = require('express');  
var app = express();  
var routes = require('./routes')(app);  
app.listen(3000);  
// routes/index.js
var hello_route = require('./hello'); // contains a single route per files  
module.exports = function (app) {  
    app.get('/', hello_route);
};
// routes/hello.js
module.exports = function (req, res) {  
    res.send('Hello world');
};

This approach is much better. Each route is clearly defined and seperated from any others and your routes are attached in isolation of your application runner. Here, you can "read" the structure of an application without having to dig into it's code: a massive plus for increasing developer maintainence effeciency. You can also change and test individual components.

I'm happy with this setup, it's the most common I see however it requires that the application dependency (app) is manually passed to every single module that requires it. This is more painful than it might appear. If it was just your route/index.js module, fine. What happens when you need to set environment configuration that the databse needs?

var db = require('./lib/db')(app);  

You now have a dependency on your app instance in every single module that requires your db...

Apply this to your logger module, your foursquare library and you will be forced to manually move that singleton around every part of your application. Not to mention the pain when 3 months into development a module that never required it now does. 148 files referencing it? Ouch. Pain point.

Why don't we modularize our application?

The "nailed it" pattern

This approach creates an application singleton which isn't passed, but requested, where needed. Exactly like you would with any external module.

// index.js
var express = require('express');  
var app = express();

module.exports = exports = app; // nailed it

app.set('routes', require('./routes'));  
app.listen(3000);  
// routes/index.js
var app = require('../');  
var hello_route = require('./hello');  
app.get('/', hello_route);  

The application doesn't need to know anything about where it might be used, nor does a module need to be restructured when it does require your app instance. You follow the standard require pattern! This has huge benefits for configuration and modularizing your code. You can now write code like this throughout your application:

// lib/database.js
var app = require('../');  
var mongo = require('mongo');  
mongo.connect(app.get('db-url'));  
module.exports = exports = mongo;  

The single biggest improvement is allowing environment configuration to be easily managed and used anywhere in your application. There are good examples you can check out implementing this pattern in real projects.

In practice, when using this pattern (as those examples do) you often see the express intance wrapped within a custom application instance. This allows the abstraction of express from your application code to decrease your dependency on the framework and its internal systems. It should nearly always make sense to do so within your own project.

If you're not following this pattern, I highly recomend given it a try to see where it can simplify your applications internal dependency management.