Easiest way to build an HTTP server using Node
Yousuf Ejaz Ahmad
Posted on August 28, 2021
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}`
));
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');
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 => {
})
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 )
})
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)
})
})
Upon running node index.js in the terminal, you would see something similar:
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)
})
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)
})
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:
And we are finished!
Posted on August 28, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.