Unpacking NodeJS

Jakob Persons
6 min readAug 12, 2024

--

NodeJS

What is NodeJS?

NodeJS is an open source Javascript runtime environment that gives developers the ability to create servers, web applications, and command line tools and scripts. Node is great for fast, scalable data intensive apps, has a vast amount of libraries available to use through NPM (Node Package Manager), an extremely active developer community and allows for the use of Javascript across your entire tech stack. Using Node, you can create full web applications using server side rendering or support your front end application with a backend API. In any case, Node’s popularity speaks to it’s power and usability amongst developers.

For more in depth information about Node you can check the official documentation. Node also offers command line support for documentation from within the Node repl. The Node repl is a command line tool that you can use to not only run javascript code, but it provides access to things like global variables available, and the properties that come with them.

Global variables and classes available from Node, as shown in Node repl. Open the Node repl by running “node” from your command line.

To view the properties and methods of a global variable within the repl, simple enter the variable into the repl prompt followed by a period and push Tab twice.

Global Object class properties as shown in Node repl

How does NodeJS work?

Node is built on the Google’s open source V8 javascript engine, which is built using C++. Simply put, the V8 engine compiles javascript code into machine code. Libuv is also used to run Node. Libuv is a support library that focuses on asynchronous I/O (input/output). Why is this important to know?

Node differs from other runtime environments like Django(Python), Ruby on Rails(Ruby), Golang with Goroutines and Spring Boot(Java) in that it runs on a single thread. You can think of a thread like a sequence of instructions that your machine uses to execute code. Due to the single threaded nature of Node, the execution of your code can become blocked by processes that require heavy lifting. For example, if you are reading the contents of a file in a single thread, the rest of your code execution will be blocked until that task has completed. Luckily, Libuv provides a solution by providing a “thread pool” that allows for these tasks to be completed in the background. This, and the event driven architecture of Node allow developers to unblock their code using callbacks, Promises (ES6) and async/await (ES8).

Single Thread Process

NodeJS single thread and thread pool diagram

There are several steps to the single thread process within NodeJS. Let’s focus on the meat and potatoes, where the event loop gets started. This is where the asynchronous magic happens. Imagine that you are trying to request some data from your database, and you’d like to do so without halting execution of other code within your program. We can do so because the thread pool and event loop support this ability.

The event loop basically listens for certain events to take place, like your query to the database that has finished and responded with your data, or if you’re making an http request and it’s been resolved.

During the single thread process, callback functions are registered with specific events. These callback functions are not executed within the single thread, but within the event loop whenever the event that it’s registered with is emitted to the event loop. These “heavy” processes are handle using the thread pool rather than being executed in the single thread, therefore freeing up the single thread to continue top-level code execution while other tasks are processed in the background.

Other frameworks, like those mentioned before, utilize multiple threads which allows for different tasks to be run concurrently and prevent blocking code and ultimately decreasing performance for your users. NodeJS’s event driven architecture and the introduction of Promises and async/await allows NodeJS to operate with a single thread, making it much more lightweight than other frameworks.

Modules

Node is built around the concept of modules. Modules are reusable blocks of code that you can use within your application. By using another module, you will have access to everything that is exported out of it, which could include classes, functions or variables. NodeJS uses the CommonJS module system by default, but you can configure your application to use the ECMA standard.

There are three different types of modules:

  1. Core Modules: built in modules that come with NodeJS. The fs and http modules are examples of core modules.
  2. Local Modules: these modules are are typically javascript files within your application. For example, you could have a utility function that sums two numbers together. Extracting that into its own file (each javascript file in your node application is considered a module), you can define the exports property for that module to make the sum function available elsewhere in your application.
  3. Third Party Modules: these modules are installed to your Node application using NPM (Node Package Manager), which comes installed with Node. NPM serves as a CLI application and a repository for many usable packages.

Dependencies

By installing NPM packages into your Node application, you are creating dependencies for your application to run. One very popular example is Express, a web framework for NodeJS. Say you were using Express to help run your Node server and handle routing, if you tried to do so without installing Express as a dependency then your code would not work.

Dependencies are listed and managed in the package.json file within the root of your Node project. The package.json file contains information about your project, such as the name, version number, entry point, scripts and of course your dependencies.

There are two types of dependencies. The first are regular dependencies, which are typically libraries that you can use for some specific functionality within your project. Express would be another example for a regular dependency. Dev dependencies however, are typically libraries that provide some assistance with the development process. For example, Nodemon is an NPM package that puts your Node project in a wrapper of sorts and provides a command line tool that you can use to run your application. What makes it useful though is that Nodemon watches the files within your project, and when they change your local server will automatically restart with the new changes. These types of packages will be listed in the devDependencies section of your package.json file.

Two last things about modules and dependencies. Along with your package.json file, you will also notice a file named package-lock.json. This file should not be manually changed, and is automatically generated. It contains information about all the dependencies that your application relies on, and the information about the dependencies of those packages.

Each package you install as a dependency in your project, will also have it’s own dependencies that it relies on. The node_modules folder will house all the packages that your dependencies are dependent on. This file is also automatically generated, and should not be shared or checked into Github repositories. The reason being is that nobody else will need to know what could be thousands of dependencies that your project uses. If they want to run your project on their local machine, all they have to do is install the dependences using npm install . This will find and install all dependencies listed in your package.json file, as well as install all the dependencies that they rely on in your node_modules folder.

What’s Next?

While I could have gone into much further detail regarding Node, how it works, event driven architecture, asynchronous programming and NPM…this should serve as a high level understanding of what is happening under the hood with Node.

Whether you are building an entire web application, or just need a simple high performing API that can scale without too much work…Node is a strong solution candidate. Especially if you are using a Javascript framework to build your frontend. Personally, I see the use of javascript across the entire tech stack as a huge plus because understanding Node is relatively simple. However, switching between languages to support your frontend and backend can create complications and challenges with development.

Checkout the NodeJS documentation for more in depth information about the discussed topics and any other questions you have regarding NodeJS!

Happy Coding :)

--

--

Jakob Persons

Software Engineer | Full Stack Developer | Soccer Fanatic