Organizing Express Routes with a Route Loader
Tags: node, express, router
Published: 2023-02-24 08:31:16


## Introduction
Express Js is one of the most widely used Node frameworks for developing web applications. Its robust features and minimalistic capabilities have made it popular with developers for its easy-to-use nature. However, as an application grows in complexity, organizing the code can become a challenge. Express doesn't impose any folder or architectural structure, which can lead to a cluttered and hard-to-manage codebase.

## The Problem
As your application grows, the complexity of your API will grow in tandem. To organize your code, you may want to split up your routes into multiple files. You can structure your code based on technical responsibilities (e.g. controllers, models, services, utils, tests) or domain modules (e.g. users, orders, products). However, manually importing all your routes one by one into the app.js file is not ideal.

## The Solution (using ESM)
The problem of splitting the routes into multiple files can be solved by using a Route Loader. A Route Loader is a function that takes a glob pattern and automatically imports all your route files. You only need to call the function once in your app.js file, and it will take care of the rest.

```

import { Router } from 'express';
import glob from 'fast-glob';
import fs from 'fs';
import path from 'path';

const BASE_DIR = path.join(__dirname, '..');

export default async function RouteLoader(globPattern) {
  let router = Router();
  let files = [];
  try {
    files = await glob(globPattern, { cwd: BASE_DIR });
  } catch (error) {
    console.error(error);
  }

  for (const file of files) {
    if (fs.statSync(file).isFile() && path.extname(file).toLowerCase() === '.js') {
      try {
        const routeModule = await import(path.resolve(file));
        router = (routeModule.default || routeModule)(router);
      } catch (e) {
        throw new Error(`Error when loading route file: ${file} [ ${e.toString()} ]`);
      }
    }
  }

  return router;
}

```
With this Route Loader, you can split up your routes into multiple files and organize them in a way that makes sense for your application. You only need to call the RouteLoader function once in your app.js file.
Here's an example of using the RouteLoader function in your main Express application file (usually named app.js or index.js). Let's assume you have your routes organized in a directory called "routes", with each file representing a different endpoint or resource. You can use the RouteLoader function to load all these routes into your application with the following code:

```
const express = require('express');
import RouteLoader from './RouteLoader';

const app = express();

const routes = await RouteLoader('routes/**/*.js');
app.use('/', routes);

app.listen(3000, () => {
    console.log('Example app listening on port 3000!');
});

```
Your router should be defined like this:

```
export default function (router) {
    // create a new user
    router.post('/api/users/create', passport.authenticate('jwt', { session: false }), async function (req, res) { 
        /** your route implementation here*/
    });
    return router; 
}

```

In this example, RouteLoader will look for all files with a .js extension in the "routes" directory and its subdirectories and load them as routes into the Express application. The use method on the app object is used to mount the routes on a specific path, in this case, the root / path.
You can also organize your routes by domain modules, for example, you can have separate files for handling users, orders, and products in separate files within the "routes" directory:

routes/
  |- users.js
  |- orders.js
  |- products.js

In this case, you can modify the RouteLoader function to look for routes in the routes directory only, rather than its subdirectories:
`const routes = await RouteLoader('routes/*.js');
`

## Conclusion
Organizing your routes is an important step in keeping your Express application maintainable and scalable. By using a Route Loader, you can split your routes into multiple files, making it easier to manage your codebase and quickly add new routes as your application grows.