A Simplified Technique for Express Routing
A technique to dynamically configure routes for an API built using frameworks such as Express or Fastify
This tutorial will focus on one of my favorite techniques for structuring the route files. This approach uses the folder and file structure to build the API.
What are we building?
Following in the footsteps of all great tutorials, we will build a todo list API. This will provide the following endpoints.
- GET /todo-item (Returns all todo items)
- CREATE /todo-item (Adds a new todo item)
- GET /todo-item[id] (returns the todo item with the id)
- UPDATE /todo-item/[id] (updates a todo item with the id)
- DELETE /todo-item/[id] (deletes the item with the id)
A todo item will have the below structure.
- id (string — unique id)
- title (string — title of the todo item)
- status (boolean —if the todo item is completed)
Note: To keep the tutorial simple, we will store the items in memory.
Basic setup
Before we proceed, lets set up an API we can play around with.
Run the following commands to make a directory and cd into it.
mkdir dynamic-routes
cd dynamic-routes
Next, run the following to initiate an npm project (the “-y” flag tells it to use default values)
npm init -y
Run the following command to install all the required packages
npm i express body-parser express-promise-router
- express: Fast, unopinionated, minimalist web framework for node.
- body-parser: Middleware for parsing incoming request bodies
- express-promise-router: wrapper for Express 4’s Router that allows middleware to return promises
Install nodemon using the below command. This will restart the server whenever a file is changed. (The “-D” flag installs it as a dev dependency)
npm i -D nodemon
Next, make an “src” folder for all of our code and add an index.js file inside.
mkdir src && touch src/index.js
Next, update the package.json file with the following. This main will tell npm where to find our entry file. and secondly, the scripts will provide us with a way to run nodemon.
{... "main": "src/index.js", // update this line"scripts": {"start": "nodemon" // add this to the scripts},...}
Finally, add the following content in index.js.
Now, you can start the script by running the following
npm start
if you go to a browser and access this http://localhost:3000/healthcheck. You should see the following message: OK.
Todo Item Controller
Another thing we need to code before we proceed is the actual todo controller. This is what the routes will execute to carry out operations.
Again, this is a super simplified version of the controller, it simply provides a set of functions for manipulating the state of an array.
make a controllers folder inside the src, and place this file there. Your folders should look something like below.
How it’s done?
The most widely used method for setting up routes is to have a single file that will set up the routers. For a larger project, this file may require all the route handler and link it to the router instance.
In our example, we have added the handlers in the same file as well.
As you can imagine, this can easily become massive. even if you segregate the functions into their own files, or combine all handlers for one route, they can quickly become spread all over the application.
make a routes folder inside the src, and place this file there. Your folders should look something like below.
To be able to hit these endpoints, update your root index.js file with the following lines.
const routes = require('./routes');app.use('/routes', routes());
This will add all the routes we have configured after /routes
How we will do it?
Let's explore the technique of dynamically building the routes based on the folder structure.
For this, first install the following dependency
npm i klaw-sync
- klaw-sync: a Node.js recursive and fast file system walker
next, make a folder called dynamic-routes, and an index.js file inside that.
mkdir src/dynamic-routes
touch src/dynamic-routes/index.js
and add the following content in it.
The technique we will use to build the routes dynamically is by starting from a base folder, we will load all files ending in “.js” extension and using the file name, set up the route.
Route files
Each route file should be named in this format.
[ROUTE].[REQUEST_TYPE].js
- ROUTE: the path of the endpoint
- REQUEST_TYPE: optional request type (GET/POST)
Each route file should expose the following
- endpointHandler (the function handling the request)
- middlewares (optional — array of middlewares to add to the route)
- requestType (optional —prioritize this request type over the one in the filename)
- endpointPath (optional — prioritize this path over the one in the filename)
Example
If we had the following folder structure.
This will build all the routes we defined initially.
Starting from the folder called “base”, everything inside it will be converted into a route. “healthcheck.js” is placed directly inside the base folder, so this will become “/healthcheck”.
Any files inside a folder will have the folder name as the prefix. i.e the file
base/todo-item/:id.delete.js
will become
DELETE /todo-item/:id
Furthermore
base/todo-item/:index.get.js
will become
GET /todo-item
Final thoughts
The biggest advantage I get by utilizing this approach is that all the information related to my route is present inside one file. This saves me time hopping between a bunch of files to find out what the request type is, the request path, middleware and etc.
Let me know what you thought about this approach.
Code
You can find the code in my Github repo.