Exploring Nodejs Series: Chapter 3 — Requests, Reponses.

Exploring Nodejs Series: Chapter 3 — Requests, Reponses.

Chapter 3, Here we go!! In this chapter, we will learn how to create a server that receives requests from the client and sends back responses, what are ports, and how to do simple routing.

What are we waiting for, LET'S DIVE IN!


We talked in the last chapter about how the web works and we learned that when we enter a URL in the browser it sends a request to the server, in which the server responds with whatever it is configured to respond with to the client.

WHAT IS A CLIENT??

Here we refer to the web browser which sent the request as the client as we will serve it (or do a service for it like clients in real life) a file which mostly is the website HTML file. so a web client is just something that gets served, CAN IT BE ANOTHER SERVER??

Yes, exactly, it doesn't have to be a user browser, maybe the server that serves us the HTML file has to request some data from a database which will be hosted (or saved) on a server, then it will have to request the data needed from the database server. In that case, the backend server (the server which serves the HTML file) is a web client.

Simply put a web client is a computer that sends requests and a web server is a computer that sends responses. But for simplicity, computers whose purpose is to serve files are referred to as servers no matter what their current state are (requesting or responding) and browsers are the clients.

I HAVE HEARD ABOUT HTTP SERVERS. WHAT ARE THOSE??

This is the server that we talked about that sends us back the html file.

WHY IS IT CALLED AN HTTP SERVER??

Well, requests and responses (web communication) is standardized by HTTP Protocol and since that server is configured to handle HTTP Requests/Responses, we call it an HTTP server.

ARE THERE OTHER KINDS OF SERVERS??

Of course, there are FTP Servers that handle requests sent under FTP (File System Protocol). They are used for transferring data (downloading or uploading files from or to the server [see just another computer]), SMTP servers (for transferring emails), database servers and so on.

OK, OK, LET'S GET BACK TO HTTP SERVERS. I WANT TO MAKE A WEBSITE, DO I HAVE TO BUY A CERTAIN COMPUTER THAT WILL WORK AS A SERVER?

No, No, any computer can work as a server, but yes, if you are serving a website in which many many people are visiting it, you will need a very capable computer to handle that many requests, computers which are very capable and are designed to be used as servers are called servers in the hardware world. We usually rent them too and don't buy them ourselves. Look at the image below.

servers

A row of computer servers in a server rack. Attribution: CSIRO. Licensed under CC-BY-3.0.

As you can see, those are several servers, and they don't have a screen because we don't need a screen, they usually have a capable CPU, much more memory and disk space than ordinary computers.

Now, we don't need a server of this kind to do a simple website just for us to view. We can create a server on our computer and use that as well, for the moment, we will create a server that serves files locally (only our computer can access). CAN I USE MY COMPUTER WHILE IT IS WORKING AS A SERVER?

Of course, just you are running multiple programs at the same time right now, computers can do multiple things at the same time.

OKAY, LET'S CREATE A SERVER.

Yes, using your favorite IDE (maybe for you, it is a simple notepad app), create a .js file with any name you like and let's get writing code.

First, we will import a core module of nodejs that contains methods (functions) for creating an http server and dealing with requests and responses, and that is the http module.

const http = require('http')

Then we will use a method on that http object (since http variable will be the reference to the object that is returned from the require function) called createServer. createServer takes a function parameter, this function is run every time a request is sent to the server.

That parameter function takes two parameters, you can specify any names to them but essentially the first param (short for parameter) will be a request object (an object containing key values pairs or information basically about the request that the server received) and nodejs handles that response object automatically so it will be available for us in the request listener/handler function (the function that is run every time a request is sent to the server). I will call the first param req and that is the convention.

The second param will be a response object also pre-configured by nodejs in which we will add our response. I will name it res.

const server = http.createServer(requestListener)

function requestListener(req, res) {
  // configure our response here
}

Note that when calling functions like require we are assigning their return value to a variable. We don't have to do that. we can chain them like this:

const server = require('http').createServer(requestListener)

But we dont do that for readability purposes and specifically for two readability reasons.

  1. any statements that import modules should be at the top of the file

  2. chaining here is not a readability problem but if there are multiple chains then the statement will get very long and not very readable

LETS GET BACK.

Alright, now that server (or to be specific that 1-service server) is not doing anything, it is just there, we have to configure it to listen for requests. WHAT DO YOU MEAN BY 1 SERVICE??

Well, typically a real server (which is a computer) will have a firewall, so when we send a request to it, by default it blocks it, on that server, you might have multiple services running (web service for providing websites, database service, mail service for sending and receiving mails) and each service has a port number associated with it, you can open ports (holes) in the firewall so that when a request is sent holding remote (server service) port number, the server will know where to direct that request (to the web service here).

When we enter a URL, the IP address of the server gets resolved by the DNS server and also the port is sent as part of the url in this format 142.251.143.100:443 where the part preceding the : is the ip address and 443 is the port number. The request object will have a headers object which contains a host key whose value is that part 142.251.143.100:443

If the port(hole in the firewall) is open, it will get sent to the service with the associated port number. Now, in our case, to do that, we configure the service (or the server since it is a 1-service server) to 'listen' on port 3000, which means it will get assigned a port number and the firewall will allow requests which hold that port number in the url to pass to the designated service.

To do that, we write

server.listen(3000)

That number is arbitrary we could use any number within a certain range 0-65,535 but ports from 0-1023 are reserved for system processes so to be in the clear, i am using 3000. WHY 65,535??

You are so curious, ok, i will tell you. The port number is of data type integer but only a 16-bit number is allowed, which means there are only 16 digits and each digit is 0 or 1 so two possibilities among 16 digits give us 65,536 possibilities and since we are counting from 0, the highest number will be 65,535.

Now returning to the topic, with those statements:

const server = http.createServer(requestListener)

function requestListener(req, res) {
  // configure our response here
}

server.listen(3000)

if we run this, the js file will keep running since there is an event listener listening for requests and its listening indefinitely. Now in the browser we have to type an address so what will it be?

We will type the ip address and the port number in the same as this 142.251.143.100:443. But what will it be?

There is a special ip address 127.0.0.1 that refers to the self (same computer we are sending that request from). It is also called the 'loopback' address. So the address will be 127.0.0.1:3000 and this will send a request to the same machine and the request will get directed to the process with the port number 3000 which is the server we created.

Now that server will run the function inside the createServer function, the request object will receive the request aaaaand currently we are not doing anything in the function so a response is never sent back the browser and browser is still waiting so we will get a blank page with a never-ending loading icon.

To progress from this state, we will configure the function to send a response. Fortunately, nodejs gives us a response object as a second parameter so we can work it.

function requestListener(req, res) {
  res.setHeader('Content-Type', 'text/html')
  res.statusCode = 200
  res.write('<html><body>HELLO FROM THE OTHER SIIIIDE</body></html>)
  res.end()
}

WHAT IS THAT??

Let me explain,

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

so now in the response, we have to tell the browser what kind of file are we sending, are we sending a video back, are we sending an html file, what is the type of the content that is sent back.

This will add a key value pair in the headers object that is part of the response object specifing that the content type of the response is html

res.statusCode = 200

this line sets status code to be 200, which in networking is a standard for 'your request has been handled successfuly and we sent you the requested response'

this line is not mandatory because it is set by default to 200 when the requested response is sent to the client

res.write('<html><body>HELLO FROM THE OTHER SIIIIDE</body></html>)

here we are writing the response body, which is the html content that will be sent back to the client

res.end()

then we need to end the response, to let the browser know that that's it and there is no additional response that will be sent. After we end it we cannot res.write() anything else because the response has been sent already and if we did this, we will get an error

WHAT IF WE WANT TO SEND DIFFERENT RESPONSES BASED ON THE URL PATH, GOOGLE.COM/MAPS RESPONSE DIFFERS FROM GOOGLE.COM

Excellent question, then we would do a simple if statement:

function requestListener(req, res) {

  if (req.url === '/') {

  res.write('main page')

  } else if (req.url === '/maps') {

    res.write('maps page')
  }
}

req.url value is the slash / after google.com in google.com/ and anything after it, be it maps or any other path. In google.com/ the path is /, in google.com/maps, path or route is /maps and that will return true when checking for strict equality. What we did is called routing, because we are sending a response based on the path (or route) in the request.


And that's it for this chapter. Hope you enjoyed it. If you have any questions, please feel free to ask them below. Additionally, I welcome any corrections or valuable information you may have to contribute.

The content of this series draws inspiration from Maximilian Schwarzmüller's Node.js course on Udemy, serving as an index for the topics covered. However, it should be noted that the content reflects my personal interpretation and additional research conducted by me.