DEV Community

Cover image for File-Based Routing in Node.js
Daniil Sitdikov
Daniil Sitdikov

Posted on • Edited on

File-Based Routing in Node.js

About a year ago, I tried Next.js for the first time and was so impressed by their file-based routing that I decided to implement a similar system on the backend. I couldn't find anything suitable, so I took it upon myself to create my own solution. It turned out to be technology-agnostic and works with both pure Node.js, Bun and the popular Express.js.

In this brief guide, we will attempt to partially implement a shop's API. This tutorial uses JavaScript and CommonJS module types to keep things simple and quick to get started.

1. Installation

πŸ’Ώ Install node-file-router:

npm install node-file-router
Enter fullscreen mode Exit fullscreen mode

2. Initialization

Create an server.js file and do the following

If you use pure Node.js:

// 1. Import default http module and node-file-router
const http = require('node:http');
const { initFileRouter } = require('node-file-router');

// 2. Create an entry-point function
async function run() {
  // 3. Initialize node-file-router and the handler function
  const useFileRouter = await initFileRouter();

  const server = http.createServer((req, res) => {
    // 4. Create a server and invoke created function on each request
    useFileRouter(req, res);
  });

  // 5. Start listening a server on 4000 port
  const port = 4000;
  server.listen(port, () =>
    console.log(`Server running at http://localhost:${port}/`)
  );
}

// 6. Run entry-point function
run();
Enter fullscreen mode Exit fullscreen mode

If you use Express.js:

const { initFileRouter } = require('node-file-router');
const express = require('express');

async function run() {
  const fileRouter = await initFileRouter();

  const app = express();

  app.listen(4004);
  app.use(fileRouter);
}

run();
Enter fullscreen mode Exit fullscreen mode

2. A first route

🏑 We will create a home route that displays welcome content.

1. Create an api folder at the root of your project.

β”œβ”€β”€ api/ <-
β”œβ”€β”€ server.js
└── package.json
Enter fullscreen mode Exit fullscreen mode

2. Create a file named index.js inside this folder:

β”œβ”€β”€ api/
β”‚  └── index.js <-
β”œβ”€β”€ server.js
└── package.json
Enter fullscreen mode Exit fullscreen mode
module.exports = function index(req, res) {
  res.end("Welcome to our shop!");
}
Enter fullscreen mode Exit fullscreen mode

3. Run a server using: node server.js command
4. Open a browser and navigate to http://localhost:4000.

You should see the message Welcome to our shop! displayed.

Congratulations! πŸŽ‰ You've created a first file route

3. Add http methods

1. Before we start, we need a small utility function which will parse json from a request.
Create a folder utils and put http.utils.js file inside.

β”œβ”€β”€ api/
β”œβ”€β”€ ...
β”œβ”€β”€ utils/
β”‚  └── http.utils.js <-
...
Enter fullscreen mode Exit fullscreen mode
module.exports = {
  parseJson(request) {
    return new Promise((resolve, reject) => {
      let data = '';

      request.on('data', (chunk) => {
        data += chunk;
      });

      request.on('end', () => {
        try {
          const parsedData = JSON.parse(data);
          resolve(parsedData);
        } catch (e) {
          reject(e);
        }
      });
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Create a folder called products and an index.js file inside it.

β”œβ”€β”€ api/
β”‚  β”œβ”€β”€ products/ <-
β”‚  β”‚  └── index.js <-
β”‚  └── ...
...
Enter fullscreen mode Exit fullscreen mode

3. Implement get and post methods:

const { parseJson } = require('../../utils/http.utils');

module.exports = {
  get: (req, res) => {
    res.end('list of products');
  },
  post: async (req, res) => {
    const newProduct = await parseJson(req);
    res.end(`a product will be created: ${JSON.stringify(newProduct)}`);
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Open a browser and go to http://localhost:4000/products.

You should see a list of products message displayed.

4. Make a POST request using curl, Postman or any tool you like on http://localhost:4000/products

The response should display a product will be created along with your content.

Perfect! πŸŽ‰ Let's move on

4. Dynamic routes

1. Create a new file with the name [id] inside the product folder.

β”œβ”€β”€ api/
β”‚  β”œβ”€β”€ products/
β”‚  β”‚  β”œβ”€β”€ ...
β”‚  β”‚  └── [id].js <-
β”‚  └── index.js
...
Enter fullscreen mode Exit fullscreen mode

2. Fill it the same way as you did before:

module.exports = {
  // Add the `routeParams` argument as the final argument to the function. This argument will contain
  // all the taken route parameters.
  get: (req, res, routeParams) => {
    const { id } = routeParams;
    res.end(`product ${id} info`);
  }
};
Enter fullscreen mode Exit fullscreen mode

3. Open a browser and go to http://localhost:4000/products/123.

The page should display a message: product 123.

Alright, let's make it more sophisticated.

Say, we want to address the following case:
/catalog/tag-1/tag-2/tag-n

4. Create a catalog folder with [[...categories]].js inside.

β”œβ”€β”€ api/
β”‚  β”œβ”€β”€ catalog/
β”‚  β”‚  β”œβ”€β”€ ...
β”‚  β”‚  └── [[...categories]].js <-
β”‚  β”œβ”€β”€ index.js
...
Enter fullscreen mode Exit fullscreen mode

And add a single get method:

module.exports = {
  get: (req, res, routeParams) => {
    const { categories } = routeParams;

    // This type of route also covers just "/catalog"
    if (!categories) {
      return res.end('all products');
    }

    res.end(`get products that have such tags: ${categories}`);
  },
};
Enter fullscreen mode Exit fullscreen mode

5. Open a browser and go to http://localhost:4000/catalog/men/sneakers/nike.

The page should display a list of categories: men, sneakers, nike

πŸ₯ That's it!

What's next?

  1. Discover more capabilities of the routing system
  2. Configuration and More Details
  3. Examples with Express, WebSockets, ECMAScript, and more
  4. How to create your own custom adapter and use it with your favourite framework
  5. Usage with Bun

Top comments (3)

Collapse
 
parzival_computer profile image
Parzival

It's amazing to see how you were inspired by Next.js' file-based routing and took it upon yourself to create a technology-agnostic solution for the backend, compatible with both Node.js and Express.js. This tutorial is a valuable resource for anyone looking to implement a shop's API. Good job!

Collapse
 
nadja_koroleva_60b2428a54 profile image
Nadja Koroleva

Wow, thank you so much!! No more complicated β€œifs” and RegExps. I got used to the router in Next.js, too and i’m happy that i can do the same on the backend

Collapse
 
eskel profile image
David Spohr

Great, but how do I also serve static files like images?