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;
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;
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");
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;
});
};
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;
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)
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 sayroutes/bills/all.js
androutes/bills/monthly.js
it would be amazing to havebills
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?
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.