Algobook
- The developer's handbook
mode-switch
back-button
Buy Me A Coffee
Fri Mar 31 2023

How to structure express routers in a large project

So you started a while ago with creating your backend API with a few endpoints, then in a steady flow, it kept on growing. And now, the backend part of your application is a mess and the endpoints are basically in one super large file. Don't sweat it, we've all been there.. But let us take a moment and see if we can clean it up. In this tutorial, we will share some good tips on how we can structure our router.

Problem statement

Let us imagine we have a webshop application that contains of three "areas" of endpoints. Example: User accounts, products, and orders. And our endpoints looks like this:

webshop.com/api/v1/account GET, POST webshop.com/api/v1/product GET, POST webshop.com/api/v1/order GET, POST

Now, we want to structure them in a good way so that we can create a scalable solution that can grow together with our application.

Example in code

Let's create our file structure. Image below shows how it will look like.

image of file struture

So, we have our index.js and then a folder called router with a mainRouter.js file. Then we have three sub-folders with a router in each of them.

Now, let's add our code.

index.js

const express = require("express"); const { mainRouter } = require("./router/mainRouter"); const app = express(); // Base URL app.use("/api/v1", mainRouter); app.listen(8081, function () { console.log("Web server listening on port 8081"); });

mainRouter.js

const express = require("express"); const { accountRouter } = require("./account/accountRouter"); const { orderRouter } = require("./order/orderRouter"); const { productRouter } = require("./product/productRouter"); const mainRouter = express.Router({ mergeParams: true }); // Routes to our sub-routers mainRouter.use("/product", productRouter); mainRouter.use("/account", accountRouter); mainRouter.use("/order", orderRouter); module.exports = { mainRouter };

accountRouter.js

const express = require("express"); const accountRouter = express.Router({ mergeParams: true }); accountRouter.get("/:id", (req, res) => { res.send({ account: { id: req.params.id, username: "testaccount" } }); }); accountRouter.post("/createAccount", (req, res) => { // Implementation }); module.exports = { accountRouter };

orderRouter.js

const express = require("express"); const orderRouter = express.Router({ mergeParams: true }); orderRouter.get("/:id", (req, res) => { res.send({ order: { id: req.params.id, status: "ONGOING" } }); }); orderRouter.post("/addOrder", (req, res) => { // Implementation }); module.exports = { orderRouter };

productRouter.js

const express = require("express"); const productRouter = express.Router({ mergeParams: true }); productRouter.get("/:id", (req, res) => { res.send({ product: { name: "Computer", price: 500 } }); }); productRouter.post("/addProduct", (req, res) => { // Implementation }); module.exports = { productRouter };

All right, now we have created all our routers and handlers. Let do some examples.

GET localhost:8081/api/v1/product/123

Will return:

{ "product": { "name": "Computer", "price": 500 } }
GET localhost:8081/api/v1/order/123

Will return:

{ "order": { "id": "123", "status": "ONGOING" } }

Some notes

You might have noticed that we create our routers like this:

const productRouter = express.Router({ mergeParams: true });

The mergeParams flag will make sure that the req.params will be preserved to the child router. In our example it does not matter, but if we for example would include a new router inside of, let say, our product router. Then in order to get the /:id param to the child router, mergeParams: true must be set.

Example

Let's expand our productRouter.js

const express = require("express"); const { specialOfferRouter } = require("./special-offer/specialOfferRouter"); const productRouter = express.Router({ mergeParams: true }); productRouter.use("/:id/specialOffer", specialOfferRouter); productRouter.get("/:id", (req, res) => { res.send({ product: { name: "Computer", price: 500 } }); }); productRouter.post("/addProduct", (req, res) => { // Implementation }); module.exports = { productRouter };

Create our specialOfferRouter.js

const express = require("express"); const specialOfferRouter = express.Router({ mergeParams: true }); specialOfferRouter.get("/", (req, res) => { res.send({ specialOffer: { name: "Computer", price: 200, productId: req.params.id }, }); }); module.exports = { specialOfferRouter };

If mergeParams is not set to true here, req.params.id will be undefined.

Outro

In this guide, we shared an approach of how we can make our express router easy scalable and created in chunks. Instead of having large file with messy endpoints, we instead keep each router small and to only care about its "area".

  • Do you have any good suggestions of how you would have structured it? Feel free to send us any feedback!

Hope you enjoyed this post, and I hope it could help you in your project as well.

All the best!

signatureFri Mar 31 2023
See all our articles