Modular Express.js routes

Let's say we should build a big REST API in Express 4.x.
Wouldn't it be nice to have each sub-route in a separate class? Of course it would!

This can easily be achieved. In this (not big at all) example I am using Node 6+ to be able to use default parameters (which I think is great for DI and testing).

./server.js

Just starts the app. Nothing interesting, please continue :)

const app = require('./app');  
app.start();  
./app.js

This is the main app that defines our routes and starts the Express-server.

const express = require('express');  
const PostRouter = require('./routes/posts');  
const AuthorRouter = require('./routes/authors');

/**
 * @param {Object?} app 
 */
module.exports = function start(app = express()) {  
  app
    .use('/posts', PostRouter.create())
    .use('/authors', AuthourRouter.create());
  // ... add middlewares for global error handling etc.
  const port = process.env.PORT || 8080;
  return app.listen(port, () => console.log(`started at localhost:${port}`));
};

We create a app-instance by using a default parameter.
The point of doing this (can be achieved in other ways), is that we from a unit test can pass whatever we want as an app.

We are calling app.use and register our sub-routes with
instances of express.Router. The first parameter is the name
of the sub-route (e.g every request under /posts should be handled by
"post" route). The PostRouter is a standard ES6 class and the PostRouter.create method is used as a factory that creates the express.Router and delegates each request to a PostRouter instance method.

./routes/posts/index.js
const { Router } = require('express');  
const { mongoClient } = require('../mongo-client');

module.exports = class PostRouter {  
  /**
   * @param {Object?} api - Express router
   * @return {Object} express router
   */
  static create(api = Router()) {
    const router = new PostRouter();

    api.get('/', (req, res) => router.getAll(req, res));
    api.get('/:post_id', (req, res) => router.getById(req, res);

    return api;
  }

  /**
   * @param {Object?} db (for testing)
   */ 
  constructor(db = mongoClient) {
    this.db = db;
  }

  getAll(req, res) {
    return this.db.collection('posts')
     .then(posts => res.status(200).send(posts))
     .catch(err => res.status(err.status).send(err));
  }

  getById(req, res) {
    const { post_id } = req.params;
    return this.db.collection('posts').find({ id: post_id })
     .then(post => res.status(200).send(post))
     .catch(err => res.status(err.status).send(err));
  }
};

By creating a instance of the PostRouter class in the static create
method and delegate the express.Router handler to the instance methods I think that it is easier to reason about the code.

So, instead of saying:

We have a problem in the get post by id handler

we can say:

We have a problem in the getById method in the PostRouter class.

(the later should be the better, even if its takes longer time to say...)

And also, it is easier to write unit test for the route.
Which I will show in my next post.