Node.js Backend Development - Implementing Routing
In the previous article in this series on developing backend applications with Node.js, we learned how to work with URLs, request methods, and forms. The code of all the articles can be found on the Github repository Learn Node.js With Sabuj. Code for each article is separated into branches on the repository. Code for the current article can be found on the branch named "006_implementing_routing".
Before the fifth article in this series our application was quite dumb. No matter what URL we requested, we were presented with a simple HTML page that was loaded from the disk. In the fifth article we made it a bit dynamic by conditionally serving different pages. The responsibility of the HTML form serving was separated and the form submission URL was separated. Any other requests were served with serving the home.html. Though it is still dumb, now it can act conditionally. But imagine what would happen if we have thousands of different types of things to be served conditionally by thousands of different if-else blocks! So, we need a smarter system. We need a routing system where we will specify different URL patterns along with their handlers.
First of all, clone the repository and checkout to the branch of the previous article. Create a file named routes.js. In this file we will create a variable called routes. The routes variable will be a variable of an array that will contain plain JavaScript objects for URL routes. These JavaScript objects will have a key pattern and a key handler. The pattern key will contain a value that will indicate a URL pattern by a string.
When a request will come to the application, the application will start to match the pattern with the received URL. If it finds a match, further search will stop. If it does not find, then it will issue a 404 Not Found response. Remember one thing very carefully—the forward slash for the pattern and the incoming URL will be ignored.
To keep our command line clean, let's remove all the console.log() and keep only the URL the request had and what request method was used.
exports.serveRequests = function(request, response){
const url_comps = url.parse(request.url, true);
console.log(request.method + " " + request.url);
if (url_comps.pathname === "/form.html"){
plainFileResponse('html/form.html', response);
}else if(url_comps.pathname === "/submit-form"){
handleSimpleForm(url_comps, request, response);
}else{
plainFileResponse('html/home.html', response);
}
};
We also need to import the routes.js to get the routes variable. 'routes.js' looks like below now:
var routes = [
]
exports.routes = routes;
In the responder.js import routes.js and extract the routes variable in the following way:
var routes = require("../routes").routes;
We usually call request handlers as views. Let's create a directory called views in the root directory. A handler or a view will accept three parameters: request, response, and url. url is the url object that we get after parsing the request URL. Previously we had plainFileResponse() and handleSimpleForm() inside responder.js. We want to move them to another module named utils inside the custom_modules. Let's create it.
utils.js
var fs = require('fs');
exports.plainFileResponse = function plainFileResponse(file_name, response){
fs.readFile(file_name, function (error, file_data){
if (error){
response.writeHead(500, "Internal Server Error",
{
'Content-Type': 'text/html'
}
);
response.write("<b>Something Went Terribly Wrong</b>", "utf8");
response.end();
}else{
response.writeHead(200, "All is well",
{
'Content-Type': 'text/html'
}
);
response.write(file_data, "utf8");
response.end();
}
});
}
exports.handleSimpleForm = function handleSimpleForm(url_comps, request, response){
var params = url_comps.query;
var name = params['name'];
var profession = params['profession'];
var result = "Profession: " + profession + "\n" + "Name: " + name + "\n"
response.writeHead(200, "All is well",
{
'Content-Type': 'text/plain'
}
);
response.write(result, "utf8");
response.end();
}
Now, we can reuse it whenever we want it from any part of the application. To serve the home.html we are going to create a view inside the views directory named home.js.
var utils = require('../custom_modules/utils');
exports.home = function(request, response, url){
utils.plainFileResponse('html/home.html', response);
}
That's all we need to create the home view. Now we can put this inside the routes. Inside routes.js we need to import the home handler function first.
var home = require("./views/home").home;
Now our routes array will look like below:
var home = require("./views/home").home;
var routes = [
{
pattern: '',
handler: home
}
]
exports.routes = routes;
Notice that the home of a website has no path and thus we have provided an empty string as the pattern.
Now, it's time to write some code for processing requests according to the route configuration.
var fs = require('fs');
var url = require('url');
var routes = require("../routes").routes;
exports.serveRequests = function(request, response){
const url_comps = url.parse(request.url, true);
console.log(request.method + " " + request.url);
var path = url_comps.pathname;
if (path.charAt(0) === '/'){
path = path.slice(1);
}
for (var i = 0; i < routes.length; i++)
{
var router = routes[i];
var pattern = router.pattern;
var handler = router.handler;
if (pattern.charAt(0) === '/'){
pattern = pattern.slice(1);
}
if (path === pattern){
handler(request, response, url_comps);
break;
}
};
};
Now, start the application and go to the browser to test if things are still working. For now, we are just expecting the home page to work properly. Ah! It's working perfectly on my machine.
Now, we need to create views for the forms too. We need to create two views—one for showing the form and the other is for processing the form. So, I am creating two files, form.js and process_form.js.
form.js
var utils = require('../custom_modules/utils');
exports.form = function(request, response, url){
utils.plainFileResponse('html/form.html', response);
}
process_form.js
var utils = require('../custom_modules/utils');
exports.process_form = function(request, response, url){
utils.handleSimpleForm(url, request, response);
}
So, we have to add them to our routes array too:
var home = require("./views/home").home;
var form = require("./views/form").form;
var process_form = require("./views/process_form").process_form;
var routes = [
{
pattern: '',
handler: home
},
{
pattern: 'form',
handler: form
},
{
pattern: 'submit-form',
handler: process_form
}
]
exports.routes = routes;
Notice that we have removed the ugly URL for the form. Now we no longer need to browse form.html, instead we only need to use form.
Now restart the application, go to the browser and test everything. Until now all is well. We will improve it more in the next article. Until then keep practicing with Node.js and keep your eyes on this blog for new posts.
About the Author
My name is Md. Sabuj Sarker. I am a Software Engineer, Trainer and Writer. I have over 10 years of experience in software development, web design and development, training, writing and some other cool stuff including few years of experience in mobile application development. I am also an open source contributor. Visit my github repository with username SabujXi.
Recent Stories
Top DiscoverSDK Experts
Compare Products
Select up to three two products to compare by clicking on the compare icon () of each product.
{{compareToolModel.Error}}
{{CommentsModel.TotalCount}} Comments
Your Comment