DEV Community

lutif
lutif

Posted on • Updated on

How to write a scalable router for express in nodejs.

I have been looking for a good way to handling routing in MERN project, scalable and robust router, there were many articles but somewhat confusing for beginners so I decided to simplify it for and what else could be a good topic for the first article at dev.to.

Here we will assume we are working on a large project that provides API to front-end with a bunch of routes.

let's dive in

1. Create a folder in root directory

I prefer it naming routes, ofcourse you can name anything you like

~/routes

2. Create root router

Now, that we have a folder to hold all of our routes, we can have the root router in it. Create a router.js file in the ~/routes folder.

3. Create and export the router

const express = require("express"); 
const router = express.Router();
const subRouters = []; //will get subRouters soon
router.use(subRouters);
module.exports = router;

Enter fullscreen mode Exit fullscreen mode

Now you can import this router in your main file and let express use it.

4. Creating sub routers

In the ~/routes folder create more folders according to your need; for demo I will create a couple of folders

~/routes/auth
~/routes/profile
~/routes/chat

create routers in subfolders accordingly, for example, ~routes/auth could have login.js and register.js.
Here ~/routes/auth/register.js

const express = require("express"); 
const router = express.Router();


router.post(
  "/api/users",
  async (req, res) => {

 //your logic here 
}
module.exports = router;
Enter fullscreen mode Exit fullscreen mode

5. The Fun part- telling your root router to look for subRouters

Now that we have a couple of routers in subfolders -there can be as many as you want- we tell our root router to import them. Importing them manually one by one could be tedious also if we add any subRouter in the future we would have to come back to root router again and take care of it manually.

Import file system and path module we will use them to look take a look in the directory.

const fs = require("fs");
const path = require("path");
Enter fullscreen mode Exit fullscreen mode

Now let's get subroutes, we are defining here an empty array to hold all imported subRoutes and a function getAllSubroutes given a directory to this it will look for all files in the directory and import them in subRoutes array,

const subRouters = [];
const getAllSubroutes = (dir) => {
  fs.readdirSync(dir).forEach((file) => {
    const fullPath = path.join(dir, file);
    if (fs.lstatSync(fullPath).isDirectory()) {
      getAllSubroutes(fullPath);
    } else {
      if (fullPath !== __filename) {
        subRouters.push(require(fullPath));
      }
    }
    return subRouters;
  });
};
Enter fullscreen mode Exit fullscreen mode

if it come across a directory it will search it recursively - take note

if (fs.lstatSync(fullPath).isDirectory()) {
getAllSubrouts(fullPath);

incase you are wondering forEach is builtin array function works similar to for loop. Also , notice in else block we check if (fullPath !== __filename) this ensure we do, not import rootRouter into itslef .

6. Call getAllSubroutes

Next, we put our function to work, call it with argument of __dirname; this is global property available in every file pointing to its directory.
now your ~routes/rootRouer/router.js should look like this.

//root router

const express = require("express");
const fs = require("fs");
const path = require("path");

const router = express.Router();

const subRouters = [];
const getAllSubroutes = (dir) => {
  fs.readdirSync(dir).forEach((file) => {
    const fullPath = path.join(dir, file);
    if (fs.lstatSync(fullPath).isDirectory()) {
      getAllSubroutes(fullPath);
    } else {
      if (fullPath !== __filename) {
        subRouters.push(require(fullPath));
      }
    }
    return subRouters;
  });
};

 getAllSubroutes(__dirname)
router.use(subRouters);

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

Now, we can add or remove as many routes according to our needs, without requiring us to change anything else.
This is off-course one of the preferred ways, let me know in the comments what do you think about this method?
Thanks for reading!😊

Top comments (2)

Collapse
 
dervarga profile image
Daniel Varga

I learned a lot from this solution, it looks and works really good, I still have a question:
How would you do that, if you wanted to have the directory name in the route?

For example, in a folder routes/bills if I have two files, lets say routes/bills/all.js and routes/bills/monthly.js it would be amazing to have bills in the route name. In this case my API call would look like /api/bills/all and /api/bills/monthly
Do you have any idea how to do that?

Collapse
 
lutif profile image
lutif

you can easily do this by appending that directory name, you see in our root router
const fullPath = path.join(dir, file);
if (fs.lstatSync(fullPath).isDirectory()) {
getAllSubroutes(fullPath);
}
here we can use file name in this if block and append this to our route. in your example file variable will be 'bills' so you can pass this to getAllSubroutes() and append that before your route.