NodeJs is a powerful technology that we can use to create end-to-end applications with one language which is JavaScript and these applications can handle millions of users without blocking. we can build any application we can think of such as web applications, desktop applications, and hardware (embedded systems).
Servers
Servers are computers that are always connected to the internet and ready to receive messages from other computers. We as developers are responsible to write code to handle the messages and send something back and with NodeJS we can write code with JavaScript to do that.
The server has internal features that we want to use such as:
- Network socket – Receive and send back messages over the internet.
- Filesystem – where files such as HTML, CSS, and JavaScript are stored.
- CPU – for cryptography and optimizing hashing passwords
- Kernal – I/O management.
JavaScript cannot use the above internal features. The language that can use the above features is C++ language. NodeJs enables us to write JavaScript code that works under the hood with C++ which enables us to work with features such as the filesystem and network on the server.
JavaScript Code
With JavaScript, we can save data such as strings, numbers, and functionality that will run later on. and we can use the data by running functionality on it. Let’s see an example:
const number = 10;
function addTwo(num) {
return num + 2;
}
const result = addTwo(number );
Node Code
With the help of NodeJS, we can write JavaScript code that uses Node built-in functionality that can trigger C++ code that acts on the computer’s internal features. One of the powerful built-in features is the http which can wait for requests. Let’s see an example:
const http = require('http');
const server = http.createServer();
server.listen(80);
Notice that in order to use the “http” which is a Node built-in feature we need to import it, unlike the setTimeout which is a “Web API built-in feature” that we don’t need to import.
The http feature which is a Node feature can access the network card on the computer and can receive messages in the http format which is the format in which the browser sends requests to the server, It does it by opening a socket which is a connection to the internet for passing data.
The “http.createServer” is a command for Node internal features for setting up a network (net) that specializes in http protocol. The Libuv will link the C++ code that is written in Node with any computer’s internal structure and will open the socket for communication.
Side Note: Libuv is a pre-written C++ code that Node uses for us to be able to run Node code in any operating system.
The “http.createServer” alone is not enough since we need to specify the port of the connection. So when “http.createServer” runs it will run a C++ code, when the C++ code will finish to run it will return an object to the JavaScript code environment, which in this case we save in a variable named server. This object will have methods that we can use and by doing that we will be able to edit the instance of the http feature in Node. so the “server.listen(80)” will not do anything in JavaScript, it will only tell Node that we want the http to have the default port number 80 which will tell the socket to listen to it.
Node Methods
Because JavaScript is single-threaded and synchronized, we can’t write code that will wait for the message to arrive, so we need Node to run our JavaScript code when needed. So because we can’t tell when the messages will arrive on the server, we will wrap in a function the code that we wrote to handle the messages, and Node will trigger and run the code when a message arrives. Let’s see an example:
const http = require('http');
function handleIncomingMessages(incommingData, functionToSetOutgoingData) {
functionToSetOutgoingData.end('welcome')
}
const server = http.createServer(handleIncomingMessages);
server.listen(80);
When we pass the function “handleIncomingMessages”, we save it in Node, and it will run automatically in the “JavaScript environment” when a message arrives on the server.
This pattern is used not only for the network but for anything we need to do to use the internal computer features, including talking to a database and the filesystem. code that handles this kind of task will be set up in Node and will have a function attached to it that will be automatically triggered to run when the task is completed or has activity.
Keep in mind that Node will automatically run the function that we pass to the method (the code) at the right moment (will add the parentheses to invoke it). Node also will automatically pass the relevant arguments to the function that it will invoke (incommingData), and these arguments will be the incoming data from the client. And that’s how in the code we will have all the data that we need the code to work on.
Sometimes Node, besides passing the relevant data as arguments, will pass a set of functions in an object that will give us direct access to the message (in Node) being sent back to the client and allows us to add data to that message (functionToSetOutgoingData).
When a message arrives on the server, the Node will:
- Immediately start an HTTP message that is ready to be sent back to the client.
- Create 2 JavaScript objects that are available in the JavaScript environment (execution context):
- Information from the arrived message (url, headers…). (incomingData argument)
- functions that we can use to pass data to the HTTP message that is ready to be sent back to the client. (functionToSetOutgoingData argument)
After that Node will automatically invoke the handleIncomingMessages function and will insert automatically the arguments (the 2 JavaScript objects that Node created) that give us access to the data of the incoming message and access to the outgoing message which are in Node.
summary
Let’s demonstrate a scenario to summarize what we learned.
Let’s say that our server has a domain named “my-quotes.com” and the server holds a collection of quotes. the client wants to get the second quote in the collection and for that, it will send a request to the server by using the HTTP protocol that handles communication between a server and a client. the request will look like this: my-quotes.com/quotes/2.
The HTTP request will look like this:
- Request line: method and the path (GET /quotes/2).
- Headers: metadata about the client computer: browser type, location…
- Body: extensive data (quote we want to add/update to the database), since it’s a GET method, it won’t have a body.
The code for this will look like the below code:
const http = require('http');
const quotes = [
'Be yourself; everyone else is already taken',
'So many books, so little time',
'Without music, life would be a mistake'
]
function handleIncomingMessages(incommingData, functionToSetOutgoingData) {
const quote = incommingData.url.slice(8) - 1;
functionToSetOutgoingData.end(quotes[quote])
}
const server = http.createServer(handleIncomingMessages);
server.listen(80);
When we create the server object it will:
- Opens a socket in the internal computer features, this operation will connect the server to the internet.
- Store the handleIncomingMessages function into Node and auto-run it when a message arrives.
- create an object (in this case named server) that has a “listen” method that enables us to edit the C++ features in Node (in this case it is the port number).
By using the “listen” method on the server object we update the port number in Node which will update the opened socket in the internal features of the computer (the network feature specifically).
When the client sends a message via HTTP with the path “my-quotes.com/quotes/2” it will arrive at the server (the network card in the computer) and with the help of Libuv, it will arrive at the Node environment as a stream of text.
At that time the Node environment will take the stream of text from the incoming message and format it in a nice way to a JavaScript object (request). besides, that Node will create another JavaScript object, the outgoing message (response) which enables us to access it and add to it the information we want via functions that are attached to it (in our scenario we want to add to the outgoing message the second quote from the collection). after that, it will execute the function we passed to the createServer and pass to it the 2 created objects as arguments.
The code in the function will grab the number “2” from the “url” and we will use the “end” method to update the outgoing message in Node.