Node.js provides an http module, which can we used to create either client or server applications. Listing 1 below outlines the minimum code required to create an HTTP server.
Listing 1
const http = require('http');
http.createServer((req, res) => {
res.end("Hello there!")
}).listen(5050);
When a request is made to the server, the callback function supplied to the createServer method is executed. This callback method provides the request and response objects, which are used to receive and send data. It is import to always end the response using the end() method. Failure to do so, will result in the server hanging. The listen() method is used to specify the port number the server should listen on.
Handling Routes
The http module is a low level module, which allows you to create a server but does not provide any methods to handle routes. You will need to implement your own route handling logic, such as mapping a request to a function. Thankfully the request object contains all the information we need to implement our own route handlers. Listing 2 below shows a simple example of how to execute a statement based on the request URL.
Listing 2
const http = require('http');
http.createServer((req, res) => {
let url = req.url.toLowerCase();
if(url == "/" || url == "/index.html"){
res.end("Index page");
}
else if(url == "/aboutus.html"){
res.end("About us");
}
else if(url == "/contact.html"){
res.end("Contact us");
}else{
res.end("404 Page not found");
}
}).listen(5050);
The code above can now execute a code block based on the request URL and even show a 404 Page not found message when a route does not exist. The routes in the sample code above are all dynamic, that is the file index.html does not actually exist. Further more, index.html might contain an image or include css/javascript files, which maybe physical files. The HTTP server needs to be able to respond to requests for files that exist. Listing 3 below shows how to response to static files such as images using the fs module.
Listing 3
const http = require('http');
const fs = require('fs');
http.createServer((req, res) => {
let name = (req.url == '/') ? 'index.html' : req.url;
let file = __dirname + '/' + name;
fs.readFile(file, (err, data) => {
if(err){
let url = req.url.toLowerCase();
if(url == "/" || url == "/index.html"){
res.end("Index page");
}
else if(url == "/aboutus.html"){
res.end("About us");
}
else if(url == "/contact.html"){
res.end("Contact us");
}else{
res.end("404 Page not found");
}
}else{
res.end(data);
}
});
}).listen(5050);
The code above will first check if a file with the requested URL exists using the fs module. If the file exists, it's contents is sent to the browser. If the file does not exist, then we use dynamic routes. Executing dynamic routes using if statements makes the code prone to errors and the readability of the code is greatly affected as more routes are used. Let's refactor the code so that each route executes a callback function. To do this, we will enclose the server in a new class called HttpServer as shown in listing 4.
Listing 4
const http = require('http');
const fs = require('fs');
class HttpServer{
constructor(){
this._routes = {};
this._error = null;
}
route(url, callback){
this._routes[url] = callback;
return this;
}
error(callback){
this._error = callback;
return this;
}
listen(port){
http.createServer((req, res) => {
let name = (req.url == '/') ? 'index.html' : req.url;
let file = __dirname + '/' + name;
fs.readFile(file, (err, data) => {
if(err){
let url = req.url.toLowerCase();
if(this._routes.hasOwnProperty(url)){
this._routes[url](req, res);
}else{
this._error(new Error('404 Page Not Found'), req, res);
}
}else{
res.end(data);
}
});
}).listen(port);
}
}
var app = new HttpServer();
app.route('/aboutus.html', (req, res) => {
res.end('About us page');
}).route('/contactus.html', (req, res) => {
res.end('Contact us page');
}).error((err, req, res) => {
res.end(err.message);
}).listen(5050);
We now have a reusable class that encapsulates our HTTP server and provides us with easy to use methods. The route() method accepts two arguments, a URL and a callback function and returns the current instance, which allows us to do method chaining. When a route is registered, it is placed in a routes collection. The error() method allows us to specify a callback function to handle 404 Page Not Found.
Summary
In this article I've explained how to create a simple HTTP server that can be used to serve static and dynamic content. I've purposefully left out some code such as sending response headers to keep the code as simple as possible. In the next article, I'll expand on the HttpServer class by adding support for sessions.
-
A JavaScript Implementation Of The Logo Programming Language - Part 2
Part 2 of A Javascript Implementation Of The Logo Programming Language. In the previous article I explained how to develop a simple lexer. In this part we develop a TokenCollection class to help traverse the array of tokens returned from the lexer.
10 June 2020 - 2902 views -
A JavaScript Implementation Of The Logo Programming Language - Part 1
In this four part article, I explain how to develop the iconic Logo programming language in JavaScript. In this part, we discuss how to take a source input and convert it into a series of tokens.
06 June 2020 - 5216 views -
Generating Web API Keys
If you're building a REST API, chances are you're going to need to generate secure random API keys. In this article, I explain how to use the Node.js crypto module to generate random secure API keys.
29 May 2020 - 7913 views -
Port Scanner
A simple port scanner to scan a range of ports.
31 January 2020 - 3663 views -
Sorting Algorithms
An introduction to sorting algorithms.
29 November 2019 - 2542 views -
Reading From Console
Node.Js provides several ways to read user input from the terminal. This article explains how to use the process object to read user input.
16 July 2019 - 2992 views -
Difference Between const, let And var
This article explains the difference between const, let and var.
12 July 2019 - 2114 views