Node.js: HTTP Module

Photo by JJ Ying on Unsplash

Node.js: HTTP Module

·

5 min read

What is this HTTP all about?

The 'http' that we use as a module is actually a protocol which allows data exchange between client and server. As discussed before, 'http' is a built-in module in Node.js. It has 2 types of messages: HTTP Request and HTTP Response.

HTTP Request

This message is sent from the browser to the server, generally to request for the resource or to update the database. It contains 3 information:

  1. Start Line

    This contains information that describes how request is being sent:

    • Method(GET, POST, PUT, DELETE) used for sending the request.

    • URL to which the request is being sent.

    • Version of HTTP being used.

  2. Header

    It is an optional section of HTTP Request that contains 'key: value' pair information like content-type, content-length, host, etc. Some content types are as follows:

     Content-Type: "text/html"
     Content-Type: "text/css"
     Content-Type: "text/javascript"
     Content-Type: "text/plain"
     Content-Type: "image/svg+xml"
     Content-Type: "application/json"
    
    💡
    Notice, the content type has the format 'type/subtype'. There are many more content types with this format.
  3. Body

    This is also an optional field in HTTP Request and it is used to provide some data while sending request to the server. Mainly, data sent from user which is needed to be stored in database is kept in this field. Here also, the data/payload is specified as 'key: value' pair. Let see an example for JSON:

     {
         "name": "Giver Khadka",
         "email": "giver1234@gmail.com",
         "age": 21
     }
    

HTTP Response

This message is sent from the server to the browser, generally in respond to the HTTP Request sent by the browser. It also contains 3 information:

  1. Start Line

    This contains information that describes the condition of server response:

    • Version of HTTP being used.

    • Status Code (200, 400, 404, 500, etc.) to indicate response status.

    • Status Text (OK, Bad Request, Not Found, etc.) to describe the status code.

  2. Header

    It is an optional section of HTTP Response that contains 'key: value' pair information like content-type, content-length, connection, etc.

💡
The content-type here plays a major role on how the data will be interpreted by browser once it gets the response from server. If the data is HTML code and content-type is HTML, then browser renders the HTML data. If the content-type is plain text, then the browser prints the HTML data as it is.
  1. Body

    This is also an optional field in HTTP Response and it is used to provide data/resource to the browser. Let see an example for HTML response:

     <html>
         <head>
             <title>Second Page</title>
         </head>
         <body>
             Hello World!
         </body>
     </html>
    

Example Time

Let's see an example of HTTP module to process request and provide response to the browser from server.

const http = require("http");
let home = fs.readFileSync("./index.html");
const server = http.createServer((req, res) => {
    const url = req.url;        
    if(url == "/"){
        // Specify HTTP Response Header
        res.writeHead(200, {'content-type': 'text/html'});
        res.write(home);
        res.end();                    // Always end the response
    }
    else{
        res.writeHead(404, {'content-type': 'text/html'});
        res.write("<h1>Page Not Found</h1>");
        res.end();                    
    }
});    
server.listen(5000);

Here, we are already familiar with the 'createServer()' method from my previous blog. The new thing is that we are using 'res.writeHead()' method to specify the HTTP Response message. It's syntax is as follows:

res.writeHead(statusCode [, statusMessage] [, headerMetaData]);

Instead of directly writing the HTML code, we can specify the HTML files as response using built-in 'fs' module for clean code. Congratulations! You made it up to here.

But, wait! There are some major issues with this pure built-in 'http' module.

The above code works fine if the HTML file that you sent as response doesn't contain any URL. But, if the HTML file has URL to resources like images, CSS, JavaScript, etc. then Node.js doesn't recognize those URL. We need to manually tell the Node.js about those URLs. Let's try providing a navbar HTML file as a response.

navbar.html

<html>
    <head>
        <title>Navbar</title>
        <link rel="stylesheet" href="./styles.css" />
    </head>
    <body>
        <nav>
            <img src="./logo.svg" class="logo" alt="logo" />
            <ul class="links">
                <li><a href="#">home</a></li>
                <li><a href="#">about</a></li>
                <li><a href="#">contact</a></li>
            </ul>
        </nav>
        <script src="./script.js"></script>
    </body>
</html>

The above navbar contains URLs of CSS Style, Image, and JavaScript code. Now, let's setup our HTTP server as follows:

const http = require("http");
const fs = require("fs");
let home = fs.readFileSync("./index.html");
let navbar = fs.readFileSync("./navbar.html");

const server = http.createServer((req, res) => {
    const url = req.url;            
    if(url == "/"){
        res.writeHead(200, {'content-type': 'text/html'});
        res.write(home);
        res.end();                    // Always end the response
    }
    else if(url == "/navbar"){
        res.writeHead(200, {'content-type': 'text/html'});
        res.write(navbar);
        res.end();                    
    }
    else{
        res.writeHead(404, {'content-type': 'text/html'});
        res.write("<h1>Page Not Found</h1>");
        res.end();                    
    }
});    
server.listen(5000);

When we visit the '/navbar' page from browser, you may expect the browser to display the navbar with all the styles, image and JavaScript applied in it. But, it won't happen. In order to see the full output, we need to mention HTTP response for those URLs as well:

const http = require("http");
const fs = require("fs");
let home = fs.readFileSync("./index.html");
let navbar = fs.readFileSync("./navbar.html");
let style = fs.readFileSync("./styles.css");
let script = fs.readFileSync("./script.js");
let logo = fs.readFileSync("./logo.svg");

const server = http.createServer((req, res) => {
    const url = req.url;    
    if(url == "/"){
        res.writeHead(200, {'content-type': 'text/html'});;
        res.write(home);
        res.end();                    // Always end the response
    }
    else if(url == "/navbar"){
        res.writeHead(200, {'content-type': 'text/html'});
        res.write(navbar);
        res.end();                    
    }
    // Response for Style, Image and JavaScript inside Navbar HTML
    else if(url == "/styles.css"){
        res.writeHead(200, {'content-type': 'text/css'});
        res.write(style);
        res.end();                    
    }
    else if(url == "/browser-app.js"){
        res.writeHead(200, {'content-type': 'text/javascript'});
        res.write(script);
        res.end();                    
    }
    else if(url == "/logo.svg"){
        res.writeHead(200, {'content-type': 'image/svg+xml'});
        res.write(logo);
        res.end();                    
    }
    else{
        res.writeHead(404, {'content-type': 'text/html'});
        res.write("<h1>Page Not Found</h1>");
        res.end();                    
    }
});    
server.listen(5000);

As you can see the code length has already started to grow a lot for such simple example. Now, imagine if you need to setup HTTP server for a large project with multiple images, styles and scripts. It won't be smart to write HTTP response for each and every URLs. That would be too much frustrating.

Now, guess what? In order to solve this issue, we have Express.js which is a framework for NodeJS that will eliminate these issues and simplify our life when writing code in NodeJS. We'll be discussing about the Express.js in my next blog.