Your Guide To The Dark Arts of Nodejs Sub-Apps

Nodejs has an amazingly simple system for managing modularity in web app development. All you need to do is just place the the functionality in its own module and simply "require" it where ever it is needed. This helps to ensure the global scope is not polluted and modules can simply be modified and upgraded as needed without affecting the apps built on it.

// import module "newFunctionModule" var newFunction = require('newFunctionModule');

But here is where the Nodejs "require" system breaks down. What if the module you are importing has more than just some processing functionality? What if it includes a route or a view? Well, nodejs modules, as they currently are, cannot have their own views or routes so you would be up the creek without so much as a paddle. However, you are in luck because now you can use a sub-app to solve this problem.

Sub-apps, shmub-apps

Sub-apps are, essentially, fully fledged nodejs apps in their own right which can be embedded into other apps. For instance, imagine creating a sub-app that manages user accounts, authentication and session management. This would be a very useful piece of functionality that can be reused across several applications. Creating this functionality as a sub-app enables you to embed it into all your projects and decouple the development of this sub-app from the rest of your other projects. This way, when ever you make an update to your user management sub-app (better password storage, say) you simply push the upgrade to all your projects and have them all benefit from the changes.

My way or the Express way

So how do you create these sub-apps? Simple. A little know feature of the Express framework enables this magic. Now, you are using Express, right? Seriously, if you are doing any kind of nodejs development, you should be using Express as your foundation. It will make your life so much easier.

Here's what a typical Express app looks like

app.js

var express = require('express'); var http = require('http'); var app = express(); // set routes app.get('/frontpage', function(req, res){ // present app front page }); // set views app.set('views', __dirname + '/views'); app.set('view engine', 'jade'); http.createServer(app).listen(80, function(){ res.send('hello world!'); });

What if we had another app with its own views and routes that we wanted to make available to this app. For instance, we could have "user.js" that manages user accounts, authentication and session management.

user.js

var express = require('express'); var app = module.exports = express(); // set routes for userjs app.get('/login', function(req, res){ // present login page }); // set views for userjs app.set('views', __dirname + '/userviews'); app.set('view engine', 'jade');

Now to mount user.js as a sub-app we simply need to make some small changes to app.js

app.js

var express = require('express'); var http = require('http'); // require user.js var user = require("user"); var app = express(); // mount user.js as a sub-app app.use(user); // set routes app.get('/frontpage', function(req, res){ // present front page }); // set views app.set('views', __dirname + '/views'); app.set('view engine', 'jade'); http.createServer(app).listen(80, function(){ res.send('hello world!'); });

Now all routes available to user.js are now available inside app.js so if a user goes to "/login", app.js will use the logic available inside user.js to process the request.

Now you might be wondering what kind of memory hit you may be letting yourself in for if you have one express app loading another express app but you may be pleased to note that Express is actually intelligent enough to only load one version of itself into memory so you don't even get any kind of performance hit.

Of course, when using this technique, you need to be cognizant of clashes of routes. That is, if the app has a route similar to one declared in the sub-app, the sub-app will take precedence.

app.js

var express = require('express'); var http = require('http'); // require user.js var user = require("user"); var app = express(); // mount user.js as a sub-app app.use(user); // set routes app.get('/frontpage', function(req, res){ // present front page }); /* * login page * NOTE: This route will NOT be utilized since * user.js already has a similarly declared route */ app.get('/login', function(req, res){ // this route is usurped by user.js }); // set views app.set('views', __dirname + '/views'); app.set('view engine', 'jade'); http.createServer(app).listen(80, function(){ res.send('hello world!'); });

Sub-apps are a great way to separate the building blocks of your web apps. Breaking your web apps into significant blocks of functionality helps to maintain simplicity and focus in your development work. Sub-apps also dramatically increase developer productivity by enabling convenient reuse of significant amounts of code.