How to modularize routes with the Express Router

If you’re building in Node.js with the Express.js framework, you’ve probably used the Router class at some point in your day-to-day development. The Express Router is a powerful feature that was introduced in Express 4, a sort of mini Express application for just middleware and routing. It also gives us the ability to create modularized routers that are mountable, composable, and extensible — making your code base a lot easier to manage as your app scales.

Today I’ll walk you through an example to demonstrate its usefulness in structuring and organizing your routes.

Modularizing routes in Express API apps

Let’s say we have an e-commerce web store, and we want to build a REST API that allows consumer apps to retrieve data about our store’s products, reviews, shopping cart, and other relevant resources.

First, we’ll create a separate router instance for our API routes. Simply call express.Router() and export it.

// routes/index.js

// create new Router instance for api routes
var router = require('express').Router();

module.exports = router;

In our main app/index.js, we'll mount the router onto our Express application at the /api path with app.use().

// server/app/index.js

var app = require('express')();

// mount the router on the app
app.use('/api', require('./routes'));

module.exports = app;

Now that we’ve created our API router, we might want to start defining the REST endpoint routes for the various resources accessible through our API right in the router’s main index.js file. But since there’s a number of resources we could potentially serve, that would probably lead us to a pretty bloated file and could get messy, fast. So let’s separate concerns and split our routes into separate modules.

Using multiple instances of the Express Router

Here we call express.Router() again to create another router instance, and now we can define the individual routes pertaining to our products.

// routes/products/index.js

// create another router for getting 'product' resources
var router = require('express').Router();

// individual products routes
router.get('/', function(req, res, next) {  
   ...
});

...

module.exports = router;  

Once our products router is implemented and exported, we then mount our products router on our API router. We’ve effectively nested the products router in the API router, and all of our product routes will now be prefixed by the mount path you set on the API router (typically ‘/api’). 

By doing this, we’re able to keep a pretty clean entry point for our API router that only contains the routes to our various sub-routers at the specified paths, giving us a nice snapshot of all the routes provided at that level in our hierarchy.

// routes/index.js

// back in our API router
var router = require('express').Router();

// mount our 'products' router onto the API router
// api/products/
router.use('/products', require('./products')); 

// let's mount a few more...
router.use('/search', require('./search'));  
router.use('/cart', require('./cart'));  
router.use('/userCreation', require('./userCreation'));  
router.use('/checkout', require('./checkout'));  
router.use('/promo', require('./promo'));  
router.use('/account', ensureAuthenticated, require('./account'));  
router.use('/admin', ensureAuthenticated, require('./admin'));

module.exports = router;  

From there we can continue to implement separate Routers for other resources such as shopping cart and user account data, and mount those on our API router with router.use(). You could even nest routers another level down (or more) to further modularize routes if sections of your app calls for it.

Conclusion

As you can see, organizing your route structure in Express can be pretty easy using the Express Router. Instead of housing potentially hundreds of routes in the same file as your app grows, you can use use multiple instances of express.Router() to modularize your routes, ultimately making our codebase better organized, maintainable, and extensible.

Thoughts? Questions? Leave a comment or drop me a note!

comments powered by Disqus