Written by Oghenetega Denedo
✏️
Node.js has gained immense popularity as a flexible and robust runtime environment that excels in server-side scripting, networking, and real-time app development. While Node apps typically use module-based routing, it’s possible to implement file-based routing to enhance productivity, scalability, and more.
When developing organized web apps, routing helps ensure that your final application is well-structured and orderly. File-based routing achieves this by mapping the app’s routes to specific files. This simplifies routing and helps with code organization, which makes maintaining apps easier.
In this article, we'll explore how to implement file-based routing in your Node.js applications. This will allow you to write well-structured and maintainable code that can scale with your project's complexity.
To get the most out of this article, you'll need a basic understanding of how to build web apps with Node.js. You’ll also need to have Node.js installed on your machine, along with any code editor of your choice.
Understanding file-based routing
A file-based routing system is a method of handling routing tasks that entails mapping your application routes and endpoints to specific files. This establishes an intuitive connection between the file structure and your application's URL structure, making the process more straightforward and efficient.
In file-based routing, you structure the application routes based on the file system hierarchy, making it easier to understand, update, and expand them. When you want to modify a route, you can easily navigate to the relevant file, where you'll find an intuitive representation of your app’s URL endpoints.
As we delve deeper into the topic, we will explore the advantages and practical applications of file-based routing in Node.js applications.
Implementing file-based routing with Node File Router
Popular frameworks such as Next.js are widely recognized for how effectively they help simplify web app development through efficient, built-in, file-based routing capabilities. Although this can be convenient, these frameworks come with the disadvantageous potential for vendor lock-in.
What if you like the freedom of using file-based routing in pure Node.js or any library you choose without being limited to a specific framework? That's where Node File Router comes in.
Node File Router provides a technology-agnostic approach to incorporating file-based routing into your Node.js applications. It works seamlessly with both vanilla Node.js and libraries such as Express.js.
Some of the features supported by Node File Router include:
- Dynamic routes: Node File Router can efficiently handle dynamic URLs such as
/products/[productId]
by matching these URLs to the appropriate file paths and extracting parameters, such as[productId]
, contained in requests - CommonJS and ES modules support: Working well with both CommonJS and ES modules makes Node File Router perfect for every Node.js project
- TypeScript support: Node File Router supports TypeScript for writing type-safe routing code
- Support for other protocols: The customized adapter that comes with Node File Router allows you to run both HTTP and WebSockets with Node
- Compatibility with other frameworks and technologies: Node File Router is also compatible with Node.js web frameworks such as Express and the Bun runtime
Let's dive into how we can set up file-based routing in an Express application with the Node File Router.
Project setup
We’ll need to create a folder for the project set up the package.json and initialize it as a Node.js project. Open the terminal and enter the following commands:
mkdir file-router-demo
cd file-router-demo
npm init -y
Next, we’ll need to install Node File Router and other packages that will be used in the project:
npm install node-file-router express nodemon
Create a new server.js
file at the root of the project, which will serve as the entry point of our application. In this file, include the following code:
import express from "express";
const port = 4000;
const app = express();
app.listen(port, () => {
console.log(`Server running on <http://localhost>:${port}`);
});
Within the package.json
file, update the type
and scripts
fields. This will prompt the app to use ES modules and start the Express server with Nodemon, respectively:
{
"type": "module",
"scripts": {
"dev": "nodemon ./server.js"
}
}
So far, we’ve covered what’s required to set up our Node.js project for file-based routing. In the next section, we’ll work on configuring Node File Router to work with the Express application we set up, as well as creating routes.
Defining routes
Before we dive into creating routes, we’ll need to configure the Node File Router to work with our Expressapplication. Update the code in the server.js
file to the following:
import express from "express";
import { initFileRouter } from "node-file-router";
const port = 4000;
const app = express();
const fileRouter = await initFileRouter();
app.use(fileRouter);
app.listen(port, () => {
console.log(`Server running on <http://localhost>:${port}`);
});
To kickstart the Node File Router, we utilize the initFileRouter()
function and declare it as a global middleware that handles incoming requests according to the designated route files.
By default, the system searches for these routes in the ./api
directory. However, we can change this by including a baseDir
attribute in the options
object within the initFileRouter
function.
Create a folder named api
at the project root. Inside this folder, create a new file called index.js
and add the following code:
export default {
get: (req, res, next, routeParams) => {
res.send("Hello world!");
},
};
This code exports an object that defines a route for handling a GET request. When a request is made to the /
route, the server sends a response message of Hello world!
. If we need to handle requests made with other HTTP methods, we can easily add them to the exported object with their associated handler functions.
With this, we have successfully created our first route using file-based routing in Node.js. In the next section, we’ll learn how to work with dynamic route parameters.
Working with route parameters
Most of the time, the web applications we build are dynamic and data-driven. In such cases, whatever routing mechanism we use should support working with dynamic route parameters.
To work with dynamic parameters with the Node File Router, we first need to define our routes. Once our routes are defined, we can implement our route handlers. Let’s see how to do this now.
Create a directory structure for your routes, with each directory representing a different route segment. You can easily create a route for viewing products by following the directory structure shown below:
api/
├── products/
│ ├── [productId].js
├── index.js
The [productId]
represents a dynamic parameter. When a user visits the /products/[productId]
route, the route handler defined in the [productId].js
file will get executed.
Now that we’ve defined our routes, we can implement our route handlers. The [productId].js
file for the product viewing route should contain the following code:
export default {
get: (req, res, next, routeParams) => {
const { productId } = routeParams;
if (!productId) {
res.end("Product id not specified");
}
fetch(`https://fakestoreapi.com/products/${productId}`)
.then((response) => response.json())
.then((json) => res.json(json));
},
};
When you visit http://localhost:4000/products/1
in your browser, you should see a response similar to this: The JSON response shown contains details for a product with an id
of 1
after hitting the /products/1
endpoint. It includes product information such as its title, price, description, category, image URL, and rating.
Pros and cons of file-based routing in Node.js
Although file-based routing offers benefits to Node.js projects, it does have some drawbacks as well. Below are the advantages and disadvantages of implementing file-based routing in Node.js.
Some of the pros of file-based routing in Node.js include:
- Modularity: Using file-based routing enables us to build a modular codebase. The file for each route defines that route, making it easier to locate, edit, and update only the files we need to edit without affecting others
- Readability: Modularity also increases the readability and maintainability of the codebase. Individual files isolate routes or groups of related routes, so a developer can just check the file structure to understand how routing works in the application
- Scalability: File-based routing scales cleanly as the size of your application increases. Since you never need to make significant changes to your existing code to keep adding new route files, managing your codebase remains efficient and straightforward
Meanwhile, some cons of file-based routing in Node.js include:
- Complexity: While file-based routing can scale as your app increases in size, the file structure of particularly large-scale applications can become increasingly complex and difficult to manage. Such directories may contain many route files, eventually resulting in an unmanageable directory layout
- Onboarding challenges: Without clear standards and conventions for naming and organizing route files, the routing structure of a project may be unclear to new developers joining the project team
- Inflexibility: While a file-based routing solution can be more flexible than traditional module-based routing, it may not offer sufficient flexibility for extremely dynamic or specific routing needs. In such instances, you might have to explore alternative routing solutions offered by libraries such as Express.js, Fastify, and Adonis.js
- Limited routing features: File-based routing solutions might not support certain advanced routing features like route naming, route middleware, and the binding of parameters
Keeping these potential drawbacks in mind can help you mitigate their impact on your project as well as determine when file-based routing may not be the best option for your needs. In the next section, we’ll compare file-based and module-based routing more closely so you can better understand when to use each.
File-based routing vs. traditional module-based routing
The choice between file-based routing or traditional module-based routing mainly depends on the needs of your project. Here's a comparison table that compares and contrasts the two routing strategies:
Feature | File-based routing | Traditional module-based routing |
---|---|---|
Code organization | Routes are defined in their own files, which makes the code more organized and easier to read | Routes are defined in modules, which can be more efficient |
Ease of setup | Easier to set up and manage, especially for large applications | More complex to set up and manage |
Flexibility | More flexible, as you can easily add, remove, and modify routes by adding, removing, and modifying files | Less flexible, as you need to modify the code to change routes |
Control | Less control over how routes are mapped | More control over how routes are mapped |
Popularity | Increasingly popular in recent years | More traditional approach |
Generally speaking, file-based routing works well for smaller projects. Meanwhile, traditional module-based routing offers control for more complex applications that require somе specialized routing configurations and sometimes support for middlеwarе.
Conclusion
File-based routing in Node.js is a powerful technique that enhances codе organization, scalability, and maintainability. By adopting this approach, developers can streamline their projects, improve collaboration, and simplify routing in web applications.
Embracing file-based routing empowers developers to create flexible and resilient Node.js applications that can easily adapt to evolving requirements, ultimately paving the way for a more robust and enjoyable development experience. Happy coding!
200’s only Monitor failed and slow network requests in production
Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third party services are successful, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.
LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. Start monitoring for free.
Top comments (1)
Matt Angelosanto,
Great article !
Thanks for sharing...