Recursively Include Routes in Node.js and Express

Sick of having either massive controller files or 50 million require statements in your Express applications? Here's a simple trick to load everything with 1 line.

2 minute read

Organizing Node.js applications can be a chore. They either have massive route / controller files which cover way too many concerns or they are broken into smaller files with 50 million require statements.

Consider the following folder structure:

Project_Folder
├── config
│   ├── index.js
├── models
│   ├── Model1.js
│   ├── Model2.js
│   └── Model3.js
├── public
├── routes
│   ├── admin
│   │   └── index.js
│   ├── dash
│   └── index.js
├── views
│   ├── admin
│   │   ├── index.ect
│   │   └── accounts.ect
│   ├── dash
│   │   └── index.ect
│   └── layout.ect
├── package.json
├── README.md
└── server.js

This is great for separating individual components into easy to manage files that focus on a single task. Unfortunately, on larger projects you might end up with something like the following block in your server.js file:

require('./routes/admin/index.js');
require('./routes/admin/accounts.js');
require('./routes/admin/campaigns.js');
require('./routes/admin/idontevenknow.js');
require('./routes/dash/index.js');
require('./routes/dash/widgets.js');
require('./routes/dash/williteverend.js');
// ...

That’s a lot of boilerplate to simply include all of your routes. For the last few months I’ve been using the following routes/index.js file to save me from require spam:

// routes/index.js
var fs = require('fs'),
    validFileTypes = ['js'];

var requireFiles = function (directory, app) {
  fs.readdirSync(directory).forEach(function (fileName) {
    // Recurse if directory
    if(fs.lstatSync(directory + '/' + fileName).isDirectory()) {
      requireFiles(directory + '/' + fileName, app);
    } else {

      // Skip this file
      if(fileName === 'index.js' && directory === __dirname) return;

      // Skip unknown filetypes
      if(validFileTypes.indexOf(fileName.split('.').pop()) === -1) return;

      // Require the file.
      require(directory + '/' + fileName)(app);
    }
  })
}

module.exports = function (app) {
  requireFiles(__dirname, app);
}

The script recursively searches your ‘routes’ directory and loads all of your controller files. To load all of your controllers you simply need to require the ‘routes’ directory:

require('./routes')(app);

Your route / controller files would like something like this:

// routes/admin/accounts.js
module.exports = function (app) {
  app.get('/admin/accounts', function (req, res) {
    // Controller logic
    res.render('admin/accounts.ect');
  });
}

Enjoy!

comments powered by Disqus