Easiest way to build an HTTP server using Node

yousufejazahmad

Yousuf Ejaz Ahmad

Posted on August 28, 2021

Easiest way to build an HTTP server using Node

Before we appreciate the existence of ExpressJS, we must know how things go around without it. The most basic thing to do with ExpressJS is to build a server. Let's do that with the help of NodeJS.
We need to build something that can simply fetch data from a foreign API and handle basic HTTP requests.

Basic HTTP Server

Here's an example of a simple web server:

const HTTP = require('http');

const port = process.env.PORT || 3000

const server = HTTP.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/html');
  res.end('<h1>Joey doesnt share food!</h1>');
});

server.listen(port, () => console.log(
  `Server running on port ${port}`
));
Enter fullscreen mode Exit fullscreen mode

To begin with, we include HTTP core module in our file say ** index.js*. *The notable part however is that we do not use the newer ES6 import syntax to include the core module. This is because Node is yet to adopt ES6 completely. Furthermore, we define a variable port which is set to process.env.PORT || 3000. When hosting your application on another service (like Heroku and AWS), your host may independently configure the process.env.PORT variable for you. After all, your script runs in their environment. You may hard-code it to a specific port such as 8080 as well.

So process.env.PORT || 3000 means: whatever is in the environment variable port, or 3000 if there's nothing there. Furthermore, we create a server using the createServer function. It accepts a callback.
The callback function we pass is the one that's going to be executed upon every request that comes in. As soon as a request is received, the request event is called, providing two objects: a request and a response object.

  • Request provides the request details. Through it, we access the request headers and request data.

  • Response is used to contain the data we're going to return to the client.

Using res.statusCode = 200 we indicate a successful response.
We also set the Content-Type header. Headers are defined with the setHeader function which takes two arguments in a key, value pair manner.

res.setHeader('Content-Type', 'text/html')

We close the sent response using:

res.end('<h1>Joey doesnt share food!</h1>')

The server is set to listen on the port given by the port variable. When the server is ready, the listen callback function is called.

Yeah! I know right. That wasn't so bad.

Unfortunately, this setting gets messy as we try to add some general features to it. Let's see how that goes.

The HTTP 'GET' Request

First off, we create a file named index.js. Using this file, we make a server to retrieve data from a free API Cat Facts. This API returns the requested data in JSON format. This API allows https requests which is roughly the encrypted version of http. So, we start by including the core module https in our file.

//index.js
const HTTPS = require('https');

Enter fullscreen mode Exit fullscreen mode

All we need now is to retrieve the data from the said API. So, we call a get() method on https.
This method takes in two arguments:

  • API URL
  • Function to deal with the response sent by the API
// index.js
const HTTPS = require('https')
HTTPS
    .get( 'https://catfact.ninja/fact', res  =>  {
    })
Enter fullscreen mode Exit fullscreen mode

Moving forward, we listen for the

on('data', () => {})

and

on('end', () => {})

events inside the response object. The ‘on data’ event effectively listens and collects the data that streams back to us when the request is carried out. To carry this out, we declare a variable called data and set its initial value to that of an empty string. Then we start concatenating small bits of data as they stream, to the data string.

// index.js
const HTTPS = require('https')
HTTPS
    .get( 'https://catfact.ninja/fact', res  =>  {
        let data = ''
        res.on( 'data', bits =>  data += bits )
    })
Enter fullscreen mode Exit fullscreen mode

It is followed by the ‘on end’. You see the data we obtained here is in JSON format. We need to convert it to a JavaScript Object to perform operations on it. So, We call JSON.parse() on the data to convert it from JSON to a JavaScript object.

// index.js
const HTTPS = require('https')
HTTPS
    .get( 'https://catfact.ninja/fact', res  =>  {
        let data = ''
        res.on('data', bits  =>  data += bits )

        res.on( 'end' , () => {
            let parsedData = JSON.parse(data)
            console.log(parsedData)
        })
    })
Enter fullscreen mode Exit fullscreen mode

Upon running node index.js in the terminal, you would see something similar:
Alt Text

To catch any error that may have been caused inadvertently during the request, we listen to errors. At the end of the get() function, we add an on error event and console.log the error.

// index.js
const HTTPS = require('https')
HTTPS
    .get( 'https://catfact.ninja/fact', res  =>  {
        let data = ''
        res.on('data', bits  =>  data += bits )

        res.on( 'end' , () => {
            let parsedData = JSON.parse(data)
            console.log(parsedData)
        })
    })
    .on('error', err => {
        console.log("Error: ", err.message)
    })

Enter fullscreen mode Exit fullscreen mode

Great! So we finally made a request to a Public API (Cat API) and successfully logged in the response in our terminal. As of now, we learned how to create a server and process a GET request. Let's take things forward and combine whatever we have learned so far.

Node.js Server to render the result of a GET request

We are going to render the final parsed data through a simple HTTP server. Here we build a simple server and feed the data into the server in the way we learned earlier. We use the http core module to build the server. Because the let and const keyword declares a block-scoped variable, we need to create our server where the parsedData variable is defined. Consequently, we call the listen method where the server variable is defined. As discussed earlier, we set the status code to 200 which signifies a successful response, and set Header to text/html to receive the response in the form of text or HTML. Additionally, we also permit access from all origins to avoid a CORS error. CORS error is a topic for another discussion entirely.

//index.js

const HTTPS = require('https');
const HTTP = require('http');

const port = process.env.PORT || 3000

HTTPS
    .get( 'https://catfact.ninja/fact', res  =>  {
        let data = ''
        res.on('data', chunks  =>  data += chunks )

        res.on( 'end' , () => {
            let parsedData = JSON.parse(data)
            console.log(parsedData)
            const server = HTTP.createServer((req, res) => {
                 res.statusCode = 200;
                 res.setHeader('Content-Type', 'text/html');
                 res.setHeader('Access-Control-Allow-Origin', '*')
                 res.end(parsedData.fact);
            })

            server.listen(port, () => console.log(
                `Server running on port ${port}`
            ));

        })
    })
    .on('error', err => {
        console.log("Error: ", err.message)
    })



Enter fullscreen mode Exit fullscreen mode

The actual parsedData object returns two things namely: fact and length. But we only need the fact, so we pass parsedData.fact into res.end(). If we had set the Content-Type header to application/json, we would have to convert the parsedData object back into JSON format. In such a case, JSON.stringify() is commonly used to convert the object into JSON.

Our server is now ready to launch! We launch the server using node index.js in the terminal and observe something similar to the image below:

image.png

And we are finished!

💖 💪 🙅 🚩
yousufejazahmad
Yousuf Ejaz Ahmad

Posted on August 28, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related