Skip to content

Instantly share code, notes, and snippets.

@zcaceres
Created May 22, 2017 16:40
Show Gist options
  • Save zcaceres/909280270715b3ffd865cc4d4f579095 to your computer and use it in GitHub Desktop.
Save zcaceres/909280270715b3ffd865cc4d4f579095 to your computer and use it in GitHub Desktop.

Simplify Your Life with Express Routers

So far we've handled our requests and responses with ease. But we still haven't seen how Express solves the annoying problem of routing the many resources and pages that a modern website may have.

Routing to Sub-Resources

Let's imagine that our website has a sub-page dedicated to a collection of kittens at '/kittens'.

Easy, right? As we saw before, we could just add a route to our app like this:

      app.get('/kittens', function(request, response, next) {
          response.render(myKittenPage, myCollectionOfKittens);
      });

But what happens if we want to view individual kittens? A user might want to click on a kitten photo and arrive at an individual page with data only about that kitten.

In our database, kittens could be assigned an ID. When a user clicked a picture they would reach a kitten's page, identified by id.

Let's do this the dumb way first:

      app.get('/kittens/1', function(request, response, next) {
          response.render(myKittenOnePage, dataForKittenOne);
      });

      app.get('/kittens/2', function(request, response, next) {
          response.render(myKittenTwoPage, dataForKittenTwo);
      });

      app.get('/kittens/3', function(request, response, next) {
          response.render(myKittenThreePage, dataForKittenThree);
      });

      ... et cetera ...

You get the idea. This manual routing, which Express won't stop you from doing, quickly becomes a chore.

Even if everything works fine, it makes our app harder to maintain and understand. All these routes clutter up our app.js file and mix our apps configuration and top-level logic with tons of routes for sub-pages. Bad idea!

It would be better if could make our code more modular. In one file, we can handle our configuration and top-level routes. In another, we handle the details of our routing.

Make a Router

Express gives us a Router object that makes handling routes like these a breeze. In essence, we're going to encapsulate our routes in the same way that a good programmer would encapsulate their code in clear, separate objects and files.

Express.js makes this remarkably easy with Express.Router().

Express.Router() returns a router object that we can attach routes to. Then, we .use() the router in our Express app and voilá, our routes are configured!

Instead of adding more routes to our app.js files, let's make a new file called kittens.js.

Inside kittens.js, we'll make almost everything we need.

    // inside kittens.js
    const express = require('express'); // Import Express
    const kittens = express.Router(); // Create a Router object named kittens


    // Our route to handle requests to /kitten
    kittens.get('/', function (request, response, next) {
      let myKittenData = {}
      /* Do any database/server logic to retrieve all of our kitten data here */

      response.render(myKittenPage, myKittenData)
    });

    // Our route to handle routes to an individual kitten's page
    kittens.get('/:id', function(request, response, next) {
      let kittenID = request.params.id; // get ID of kitten from the request URL

      let myKittenData = {}
      /* Use kitten ID to do any database/server logic to retrieve individual kitten data here */

      response.render(myKittenPageTemplate, myKittenData);
    });

    module.exports = kittens; // Make sure that Kittens is available to our other files, so we can use it in our app.js

Inside our kitten.get(), we retrieve the id of the kitten we want to view based on the URL that the user requested.

Remember, our request is just an object, which has a property called params (short for 'parameters'). This parameters object will contain properties based on the routed URL. In our GET, we specify :id as one of our request's parameters.

This means that if the user clicked on the twelfth kitten in our collection which linked to '/kittens/12', we would retrieve the kittenID '12' because the routed URL would be /kittens/:id where :id is 12.

Once we retrieve our ID, we would need to run any logic to retrieve kitten data from our database. That's beyond the scope of this article. Once we have our data, we would pass it in as an object myKittenData to fill out our HTML template, myKittenPageTemplate.

Params and Query Strings

In the last example, we called the params property from our request object.

Sometimes, routes include query strings. You can spot a query string because it follows a ? in the URL.

Let's imagine we want to filter our kittens by color. Users might select to see only black cats from a form, dropdown menu, or other input.

We'll end up with a query that looks something like this '/kittens?color=black'.

Just as our request had a .params property, it contains a query property, too! Express parses our URLs and allows us to access properties like these with request.query.

request.query will return an object if there is a query string. If there isn't, that object will be empty.

    // Our route to handle routes to an individual kitten's page
    kittens.get('/', function (request, response, next) {
      let myKittenData = {}
      let kittenFilter = request.query.color // gets a the 'color' query string if it exists

      if (Object.keys(kittenFilter).length) { // If we have a filter...
        /* Do any database/server logic to retrieve kittens by COLOR here */
        response.render(myKittenPage, myFilteredKittenData) // apply filter and respond with kittens

      } else {
        /* Do any database/server logic to retrieve all of our kitten data here */
        response.render(myKittenPage, myKittenData) // respond with all kittens
      }
    });

    module.exports = kittens; // Make sure that kittens is available to our other files, so we can use it in our app.js

Export Your Router

There's just one final step. Back in our app.js file, we need to use our new router. Let's add the following:

    // inside app.js
    const kittens = require('./kittens.js'); // specify the directory path to your router file here

    app.use('/kittens', kittens);

That's it!

Now, anytime a user enters a URL in the form of '/kittens', our router will take over. They'll be sent to our '/kittens' page (the '/', or root route, in our kittens router). Plus, if any user clicks on an individual kitten, they'll be sent to the appropriate page for that kitten's ID through our Router's '/kittens/:id' route.

We've gone from dozens or hundreds of routes to one, encapsulated router. Routers make linking sub-pages or resources much easier.

When we call .use() for our Kitten router, we take our first step into the world of Express middleware. Let's see some other ways to improve our app with .use().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment