An Intuitive Way To Organize Your ExpressJS Routes

An Intuitive Way To Organize Your ExpressJS Routes

One awkward situation I encounter every time I start a new app is how I should organize my routes. I've seen tons of examples and no two examples are ever really the same. The express-generator is a useful tool for getting an app off the ground but the way it structures the couple default example routes is a bit confusing. The express generator gives you a routes/ directory in the root of your project with a couple example route files.

One nice thing it does is demonstrate how you can use express routers to plug your routes together.

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

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

module.exports = router;

You can see that they export the express router as the module itself. The two files they give us are index.js and users.js. users.js is almost always something you delete right away after generating the scaffold for your new app because it's just an example of how to send down raw data to the client.

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

/* GET users listing. */
router.get('/', function(req, res, next) {
  res.send('respond with a resource');
});

module.exports = router;

They then tie these two routes to the express app like this:

app.use('/', require('./routes'));
app.use('/users', require('./routes/users.js'));

This pattern is fine for demonstrating a few key concepts, but as far as I can tell there's no real intuitive way to keep adding routes except to keep listing the top-level routes in the app.js file and create a new route file for each top-level route. If you have a lot of routes in your app then this pattern becomes verbose and cluttered. Your routes directory would be full of route files and if a top-level route has a ton of subroutes then the file will be full of route definitions and potentially a lot of logic.

The most elegant configuration that I've found is to turn the larger routes with lots of subroutes into a directory instead of a single route file. For instance, if /users had lots of subroutes and each subroute had a lot of logic to perform then I could break up the subroutes into individual files and put them inside /routes/users/... and create a manifest index file like this:

// routes/users/index.js
var router = require('express').Router();
router.get('/get/:id', require('./get-user.js'));
router.post('/new', require('./new-user.js'));
router.post('/delete/:id', require('./delete-user.js'));
module.exports = router;

Then I can simply export a single function in those files to be used as the route handler.

module.exports = function (req, res) {
  // do stuff
};

This all works great, but what if I now want to go down a third level? Easy peasy. Let's say we want to add a favorites segment after users in the route and we want favorites to have its own set of subroutes (e.g.: /users/favorites/new).

// routes/users/index.js
var router = require('express').Router();
router.use('/favorites', require('./favorites'));
app.get('/get/:id', require('./get-user.js'));
/* ... */
module.exports = router;

Express provides .get, .post, etc. as shortcuts to filter requests based on the HTTP method. When you call .use the handler will be used for every request regardless of HTTP method. The cool part about express routes though is that you can substitute express routers in place of request handlers and setup sub-handlers on those routers.

Now favorites would be another nested directory with it's own index manifest file and route handler files. You could continue this pattern as deep as you like. I recommend never putting actual route handlers in the manifest file once you've gone this direction because it makes it harder to find route definitions. It's nice and convenient when you know all your route definitions will be listed tidily in the index.js file for every route subdirectory. When you're interested in modifying a route handler the intuitive directory structure makes it extremely easy to navigate to the code you're after.