Express.js Series: Advanced Routing Techniques for Developers

There's a little more to Express routing than simple GET and POST handling. If you've read my Express Routing - The Beginners Guide you'll know about the basic techniques. Today I'll go in some more advanced techniques that you need to know if you want to build a larger or more complex application.

Organising Routes

Once your application gets bigger and more complicated you'll need to organise it in order to keep maintainability. Writing all your routes in the app.js is a really bad idea. It's important to have a good app structure.

public/
routes/
	- index.js
    - profile.js
    - search.js
views/
	- index.jade
    - profile.jade
    - search.jade
app.js

I like the above structure because it is simple and clear where the routes and views are. The question is, how do we get this structure working in our app?

The answer is with separate JavaScript files using module.exports. If you have no clue what module.exports is and does I advise you to read my Understanding the Express app.js.

app.js

var index = require('routes/index');
var profile = require('routes/profile');
var search = require('routes/search');
...
app.use('/', index);
app.use('/profile', profile);
app.use('/search', search);

This gives us 3 routes we can work with. We can define more specific routes in the files associated with the route.

Keep in mind that routes are relative. By relative I mean that in for example profile.js the following / route actually listens for '/profile'.

routes/profile.js

var express = require('express');
var router = express.Router();
...
router.get('/', function(req, res) { ... });
...
module.exports = router;

It's important you export the router to use it in app.js.

Regular Expressions

Yes you read that right, you can use regular expressions in your routes. A normal route with a parameter would look like this:

routes/profile.js

router.get('/id/:id', function(req, res) { 
  var id = req.params.id;
  ...
});

To make this route a bit more advanced we could add a regular expression and also remove the /id/. Since the routes are relative the above example would result into /profile/id/10001 for id 10001. Let's say our id is always between 3 and 8 numeric characters long. We could rewrite our route as follows.

router.get('/:id([0-9]{3,8})', function(req, res, next) { 
  var id = req.params.id;
  ...
  if (usersdb.find(id)) { ... }
  else { next(new Error('User not found')); }
});

Our url looks even better now: /profile/10001. If our URL does not match the regular expression (ie: /profile/a21234) it will be skipped and look for the next route or the 404 function. However if it does match the route but the id does not exist, it will throw an error.

router.param()

In our latest example we had some logic in our route, we checked if the user existed within the route. There's a better way to do this. We can execute a function for a specific parameter before a route function executes. Let's rewrite our previous example using router.param.

routes/profile.js

router.param('id', function (req, res, next, id) {
  var user = usersdb.find({ _id: id }); 
  if (user) { 
  	req.user = user;
  } else { 
  	next(new Error('User not found'));
   }
  next();
});

router.get('/:id([0-9]{3,8})', function(req, res, next) { 
  var user = req.user;
  ...
});

router.param is an amazing function and can be really handy for checking ids / users or just parsing parameters. Remember to pass the parameter as 4th function parameter though!

next() is a function passed as third parameter to a route / param function. When executed it will take you to the next middleware / route. If you give next a parameter such as new Error() or just a string, it will show the user an error.

res.redirect()

The last function I want to discuss is res.redirect() it does exactly what it suggests, redirect a user (request). res.redirect is not relative unlike the profile route for example. so res.redirect('/'); will redirect the user to the homepage. You can add a 30x statuscode too if you want, but it should be the first parameter.

You might ask yourself, when will this come in handy? Well take a look at this example from the excellent Ghost blogging platform source code.

router.get(/^\/((ghost-admin|admin|wp-admin|dashboard|signin|login)\/?)$/, function (req, res) {
  res.redirect(subdir + '/ghost/');
});

No hassle with .htaccess, just a plain and simple route to redirect the user to the real admin panel.

Conclusion

These are some techniques that I didn't want to discus in my previous post because they're a bit more advanced. I hope this information can be of any use to you! If you like it make sure to send me a tweet or share this post!