How To Make Modular Routers in Express

Express makes it easy to nest routes in your routers. But I always had trouble accessing the request object’s .params when you had a long URI with multiple parameters and nested routes.

Let’s say you’re building routes for a website www.music.com. Music is organized into albums with multiple tracks. Users can click to see a track list. Then they can select a single track and see a sub-page about that specific track.

At our application level, we could first have a Router to handle any requests to our albums.

Inside our albums.js file...

So far, so good!

But what about when we add tracks to our routes? Since tracks are associated with a particular album, our routes will end up looking something like this:

www.music.com/albums/[AlbumIdHere]/tracks/[TrackIdHere]

In the spirit of Express’ modular routers, we should have a separate router for tracks. That router isn’t part of our top level application logic. We can nest it in our albums router instead.

First let’s set up the proper forwarding in our albums router.

Now let’s make our tracks router in a track.js file.

This seems like it should work. But wait a minute. To retrieve an album’s track data, we’ll need to know the album’s id using the req.params.albumId that we set up in our albums route.

If you use the code above, you’ll discover that our albumId is undefined. Our req.params does not have an albumId property inside our tracks router. Our router is broken because we can't figure out which album the user requested when our tracks router takes over.

Solve It

Express provides an elegant solution. Back in our albums router we can refactor the route that forwards requests to our nested tracks router.

Previously, we had:

albums.use('/:albumId/tracks', tracks);

Our refactoring will attach our req.params.albumdId to our req object directly. We'll next() the request, which tells Express to send the request to the next applicable route. But instead of allowing Express to choose the route, we'll include our optional third parameter to .use(), the router that we want Express to send the request to.

albums.use('/:albumId/tracks', function(req, res, next) {   req.albumId = req.params.albumId;
next()
}, tracks);

We’ve saved our param on our req object and told Express to send it directly to our tracks router.

Here’s what our albums router will look like now.

Now in tracks we can refactor to access the req.albumId property we set.

With everything refactored, we can access all the data we need.

Any time that we need to access a parameter from a parent router in its child, we can follow the same process.

  1. Attach the data we need from the parent router to our req object directly.
  2. next() in our parent router's .use() method.
  3. Set the optional third parameter in our parent’s .use() as the child router that we want the request send to.

Happy routing!


Originally published at gist.github.com.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.