Easiest way to build an HTTP server using Node
Tags: node, javascript, codenewbie, webdev
Published: 2021-08-28 07:23:49


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:

```javascript
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}`
));
``` 

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.
```javascript
//index.js
const HTTPS = require('https');

```
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

```javascript
// index.js
const HTTPS = require('https')
HTTPS
    .get( 'https://catfact.ninja/fact', res  =>  {
    })
```
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.

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

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. 

```javascript
// 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)
        })
    })
```
Upon running node **index.js** in the terminal, you would see something similar:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c5s7xns60rmxp1y93jut.png)

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.

```javascript
// 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)
    })

```
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*.


```javascript
//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)
    })



``` 
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](https://cdn.hashnode.com/res/hashnode/image/upload/v1629708202964/r5kJkCYWS.png)



#### And we are finished!