In this tutorial we will get trough some of the main concepts of Node.js. To illustrate them, we will create a simple todo list web application.
Node.js Anatomy & Key concepts
NodeJS is composed of:
- An EventLoop (using libuv)
- Some Core modules that allows NodeJS to interact with the operating system such as
- File System: filesystem module
- Net: network module
- HTTP: http module
- Stream: stream module
The core modules are defined within Node.js's source and are located in the /lib folder.
Core modules are always preferentially loaded if their identifier is passed to require(). For instance, require('http') will always return the built in HTTP module, even if there is a file by that name.
For more information, please visit: https://nodejs.org/api/
Event Loop & Non-Blocking Programming
At the heart of NodeJS stands an Event Loop.
Since most modern kernels are multi-threaded, they can handle multiple operations executing in the background. When one of these operations completes, the kernel tells Node.js so that the appropriate callback may be added to the poll queue to eventually be executed. We'll explain this in further detail later in this topic.
More About Event Loop
The following diagram shows a simplified overview of the event loop's order of operations.
Each phase has a FIFO queue of callbacks to execute. While each phase is special in its own way, generally, when the event loop enters a given phase, it will perform any operations specific to that phase, then execute callbacks in that phase's queue until the queue has been exhausted or the maximum number of callbacks has executed. When the queue has been exhausted or the callback limit is reached, the event loop will move to the next phase, and so on.
- timers: this phase executes callbacks scheduled by setTimeout() and setInterval().
- I/O callbacks: executes almost all callbacks with the exception of close callbacks, the ones scheduled by timers, and setImmediate().
- idle, prepare: only used internally.
- poll: retrieve new I/O events; node will block here when appropriate.
- check: setImmediate() callbacks are invoked here.
- close callbacks: e.g. socket.on('close', ...).
Now that you have this concept in mind, we can start crafting the todo list application.
My First Hello World HTTP Server
To create a HTTP server with Node.js, we will use the 'http' module. To do so, we need first to call the require function. This function returns a reference to the module passed as argument.
Introduction to HTTP Module
The http module allows you to create a HTTP Server using http.createServer([requestListener]).
This function takes a requestListener(request, response) function as parameter. This function will be executed when the 'request' event will be published by the server (meaning that a request is taken in charge by the server). The requestListener function has two parameters both created by the http.Server:
request is an http.IncomingMessage that can be used to access to the request informations such as
- request.headers: Key-values pairs of headers names and values
- request.method: the request method as a string
- request.url: the requested url
response is a http.ServerResponse that offers some functionalities to craft the answer such as:
- response.write(chunk[, encoding][, callback]) that allows you to write data in the response body. This method can be called multiples times until reponse.end method has been called.
- response.end([data] [, encoding] [,callback]): it signals to the server that all the reponse informations (headers and body) have been sent, then the server should consider this message as completed. The methode response.end() has to be called on each response.
- response.setHeader(name, value)
response.statusCode = 404;
After having setup the server, you can bind it to a port using the listen function.
Using HTTP Verbs
As you may know, HTTP is using some methods (also know as "verbs") such as GET, POST, PUT, DELETE. Each method represents a kind of actions that can be executed on a resource (~ something designed by an URI).
When a HTTP client (such as a web browser) makes a request this request has three main parts:
- the request line that contains the method (a.k.a verb), the path and the HTTP version
- the headers that provides information about the request
- the message body
In Node.js, this informations are available in the request object. Some of this information can be easily retrieved:
- request.method to retrieve the HTTP verb
- request.url to retrieve the path
- request.rawHeaders to retrieve an array of the headers
- request.httpVersion to retrieve the HTTP version ...
Checking each HTTP verb in that way appears somewhat cubersome and could make the code hard to read. Let's move on a framework that will help us with that boilerplate code: the Express framework.