Three things determine the workflow of a web server. These are scalability, latency, and throughput. It’s quite a complicated task to scale up/out while keeping the throughput high and the latency low. Node.js breaks this spell and uses the non-blocking way to handle the requests. It’s a runtime environment that can reach high throughput and low latency with the non-blocking way. There’s no waste of resources or time to get the I/O requests in return.

If we take an average web server, we will see new threads or even processes that appear to process every single request and give back the response. It could have a sense if it didn’t overload the server. It may seem that creating threads will load memory and CPU less than creating processes. The thing here is that a big number of threads will lead to the waste of cycles on context switching and scheduling the threads. And this leads to increased latency, limited throughput and scalability.

Node.js offers another way of handling connections. There’s an event loop made out of one thread for processing connections. A new connection makes a callback function cope with the requests with the help of non-blocking I/O calls. It can also create new threads to scale the load in case of intensive operations. A callback function allows Node to process a bigger number of connections in comparison with Java or Apache HTTP servers, Ruby on Rails or ASP.NET.

Node.js is developed on the Google Chrome V8 JavaScript engine. It doesn’t require using ES6-ES5 transpilers. You can use Node.js not only for the back-end but also for developing desktop apps using JavaScript or any other language that can be transpiled into JavaScript (eg, CoffeeScript or TypeScript).

Node.js comes with a set of libraries out of the box. You can easily access them with npm (Node package manager) command.

JavaScript background

It took about 10 days for Brendan Eich to make a JavaScript language in 1995. It was supposed to run in web browsers to turn on animations and add some more features to the DOM (document object model). In a little time, Brendan presented JavaScript to Netscape where he was a contractor.

The name for a new language was chosen because of Sun’s Java popularity. The semantics of JavaScript could remind the semantics of Java only to a little extent.

Early days of JavaScript couldn’t predict its popularity among developers. Most programmers considered it to be a waste of time as its interpreter worked much slower than the language compilers. Google Chrome V8 JS engine made a breakthrough thanks to the dynamic code optimization and real-time compilation.

Node.js was first used as a JavaScript platform for MacOS and Linux in 2009. Ryan Dahl presented it as an alternative to Apache HTTP Server. Isaac Schlueter presented NPM in 2010. Node.js for Windows first appeared in 2011. Node.js Foundation started working on Node.js since 2015.

A guide to Node.js architecture

A low-level I/O API, an event loop with a single thread and the Google V8 JS engine are combined in Node.js. Take a look below to see a reduced code example showing you the essential HTTP server template. We use ES6 arrow functions for callbacks (see => that stands for the anonymous Lambda functions).

const http = require(‘http’); line starts the HTTP module.

const hostname = ‘127.0.0.1’; line defines the hostname variable to localhost.

const port = 3000; line defines the port variable to 3000.

const server = http.createServer((req, res) => { line establishes a server with a callback function and a fat arrow function that will give back the same response to all requests.

res.statusCode = 200; line is the success response that will return to all requests.

res.setHeader(‘Content-Type’, ‘text/plain’); line defines the type of the content to be the text.

res.end(‘Hello World\n’); line provides a text response “Hello World”.

server.listen(port, hostname, () => { line informs the server to listen on localhost via a socket.

console.log(`Server running at http://${hostname}:${port}/`); line sets a callback function to display a log message after the server starts listening.

Use node command to run the code in a terminal or a console and go to localhost:3000 in any browser to see “Hello World” phrase. Press Ctrl+C in a terminal or console to stop the server. All the calls from the code example are non-blocking and asynchronous. Events invoke the functions. createServer returns a response to a client request event. Listening event invokes the listen function.

Node.js library

Node.js has a wide range of functionality thanks to its library. You could see the HTTP module in the code example that has server and client classes. There’s another module for the HTTPS functionality of the server with SSL or TLS.

The event-loop with a single thread has one disadvantage and it’s the absence of vertical scaling. One thread will use one CPU core. We all know that there may be over eight cores. If we talk about server racks, we can find 24 cores in a single rack. It won’t do any good for the event loop.

There’s a way out. You will need a bit of programming routine. You can start child processes and connect them to the parent ones the same way as popen(3) call performs with the help of child_process.spawn() together with similar methods.

Creating scalable servers is the task of a cluster module. The cluster.fork() method starts working processes that use the same parent’s server ports with the help of child_process.spawn() under the covers. The cluster master is using a round-robin algorithm to spread the incoming connections among its users.

You won’t find the routing logic in Node.js. To keep up a state of connections within a cluster, you will have to run the session and login objects somewhere besides the worker RAM.

Node.js ecosystem

The NPM library has about 500,000 free reusable code packages and this is the biggest library you can ever find online. Most of the packages contain multiple modules. A package is a folder or a library item that has a program with the description in a package.json file. A module is a program that you run with ‘require’ statements. They can be confused sometimes, so keep the difference between them in mind.

NPM handles local dependencies of a specific project together with globally installed JS tools. You will need one command to install the dependencies for a local project with the package.json file. If you deal with global installations, you will need root privileges. To access the NPM library you can use the NPM command line or third-party package managers for a better client-side experience.

Why do you need an NPM package? Using a command line for installing a new package is fast and easy. You automatically install the latest version for your environment. Of course, you can get any version you need by indicating the number. You may need the version number when an updated version of the dependency may get broken with another version of the package.

Let’s take Express framework as an example for you. You can use it for developing single or multi-page apps and even hybrid applications. You can find the Expresscode repository that is easy to clone on Github while the manuals are at https://expressjs.com/. Use npm command to get everything to the local development directory:

  • $ npm install express -save
  • -save is enabled in NPM 5.0 and newer. It informs the package manager to include the Express module to the list of dependencies in the package.json file.
  • One more way to begin using Express is installing generator express globally to use it for the creation of the app locally in a new folder:
  • $ npm install -g express-generator@4
  • $ express /tmp/foo && cd /tmp/foo
  • Install all the required dependencies with the NPM and start the server according to the package.json file contents that was created by:
  • $ npm install
  • $ npm start

It may be too hard to stop at one single package out of over 500,000. However, there are a few things to consider. The first is Express that is the oldest and most widely used Node.js framework. Another thing you may find interesting is the utilities such as grunt, browserify, bower, gulp, the streaming build system and a module bundler. Take a look at the database clients such as Mongoose, Redis, Firebase, PostgreSQL client or Pg.

To conclude, Node.js is a JavaScript runtime environment that has a non-blocking event loop with a single threading. It’s built on the Google Chrome V8 JS engine together with a low-level I/O API. Node.js applications can be scaled thanks to the cluster module. There’s a huge ecosystem with over 500,000 packages that you can install with Yarn or a command line.

Yaroslav Golovach
y