Skip to content

Instantly share code, notes, and snippets.

@isaacSennerholt
Last active September 3, 2019 20:54
Show Gist options
  • Save isaacSennerholt/bb18ee1e8b2aa0342cb9123cfac760d8 to your computer and use it in GitHub Desktop.
Save isaacSennerholt/bb18ee1e8b2aa0342cb9123cfac760d8 to your computer and use it in GitHub Desktop.
Express routing composition made clean.

Title: Express routing composition made clean.

Use Case:

  1. We have two resources, resource Businesses(B) and resource Addresses(A).
  2. We want (B)’s and (A)’s routing to be in seperate files.
  3. We want to easily compose (A) and (B) by mounting (A) as a subresource to (B).

Solution:

function mountRouteModules(expressRouter) {
    return function mount({
      resources = [],
      resourceMountPath = '/',
      parentRouteModule = expressRouter
    }) {
      resources.forEach(resource => {
        const {
          resourceModule = () => expressRouter,
          subResourceMountPath = '/',
          resourceDependencies = [],
          subResources = []
        } = resource

        if (typeof resourceModule !== 'function') {
          throw new Error('Expected resourceModule to be a function.')
        }

        const routeModule =
          resourceModule(...Object.values(resourceDependencies))

        if (!!subResources.length) {
          mount({
            resources: subResources,
            resourceMountPath: subResourceMountPath,
            parentRouteModule: routeModule
          })
        }

        parentRouteModule.use(resourceMountPath, routeModule)
    })
}

Before solution:

-- addressRoutes.js

const router = express.Router({ mergeParams: true })
module.exports = {
  router.route('/addresses').get((req, res) => {
    return addressController.getAddress(req)
  })
  return router
}

-- businessRoutes.js

const addressRoutes = require('somePath.../addressRoutes.js')
const router = express.Router()
router.use('/businesses/:business_id', addressRoutes)
module.exports = {
  router.route('/businesses').get((req, res) => {
    return businessController.getBusiness(req)
  })
  return router
}

After solution:

-- addressRoutes.js
const router = express.Router({ mergeParams: true })
module.exports = {
  router.route('/addresses').get((req, res) => {
    return addressController.getAddress(req)
  })
  return router
}

-- businessRoutes.js

const router = express.Router()
module.exports = {
  router.route('/businesses').get((req, res) => {
    return businessController.getBusiness(req)
  })
  return router
}

-- routes.js

const mountRouteModules = require('mount-route-modules')
const businessRoutes = require('somePath.../businessRoutes.js')
const addressRoutes = require('somePath.../addressRoutes.js')
const app = express()

const resources = [
{
    resourceName: 'businesses', // Solely for readability
    resourceModule: businessRoutes,
    subResourceMountPath: '/businesses/:business_id',
    resourceDependencies: [],
    subResources: [
      {
        resourceName: 'addresses', // Solely for readability
        resourceModule: addressRoutes,
        resourceDependencies: []
      }
    ]
  }
]

module.exports = mountRouteModules(app)({ resources })
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment