Skip to main content

Command Palette

Search for a command to run...

What is Node.js? JavaScript on the Server Explained

Updated
•9 min read•View as Markdown
What is Node.js? JavaScript on the Server Explained
M
I write blogs to make concept easier for beginners.

JavaScript is one of the most popular programming languages used by developers worldwide. It is a scripting language mainly used for adding interactions and dynamic behavior to websites. Earlier, JavaScript could only run inside web browsers. But nowadays, we can also run JavaScript outside the browser. 

So, in this blog, we are going to understand how JavaScript runs outside the browser with the help of Node.js. We will also learn about the working and internals of Node.js in simple and beginner-friendly words. Without wasting any more time, let’s get started.

What is Node.js

Node.js is a cross-platform, open-source JavaScript runtime environment that allows developers to execute JavaScript code outside the web browser. It was initially created by Ryan Dahl in 2009 and is currently maintained by the OpenJS Foundation.

Now, “cross-platform” means it can run on operating systems like Windows, Linux, and macOS.

“Runtime environment” means Node.js is not a programming language or a framework. Instead, it is an environment that allows JavaScript code to execute outside the browser.

Node.js is also open-source, meaning its source code is publicly available on GitHub. Because of this, developers were able to create other JavaScript runtimes such as Deno and Bun.

Why Node.js was Made

Remember I told you earlier that developers were not able to run JavaScript code outside the browser?

That is where Node.js and other JavaScript runtimes came in.

Earlier, JavaScript was mainly used for client-side development because browsers were the only environment where JavaScript could run. On the server side, developers used technologies like Java, PHP, Ruby, and Python for backend development. This created a situation where developers had to use different programming languages for frontend and backend development.

Node.js solved this problem by allowing JavaScript to run outside the browser. Because of this, developers were able to write server-side code using JavaScript as well. This enabled full-stack development using a single programming language.

How Node.js was Made

Node.js was created by Ryan Dahl. Internally, Node.js is built using the combination of:

  • V8 Engine

  • libuv

  • C/C++

These components work together to make JavaScript run outside the browser efficiently.

V8 Engine

A web browser contains many components, and one of them is a JavaScript engine that helps the browser understand and execute JavaScript code.

Google Chrome uses a JavaScript engine called V8. V8 is responsible for parsing and executing JavaScript code. It converts JavaScript into machine code so the computer can execute it very quickly.

The browser also provides things like:

  • DOM

  • Web APIs

  • fetch()

  • setTimeout()

All these together form the browser runtime environment.

Different browsers use different JavaScript engines:

  • Chrome → V8

  • Firefox → SpiderMonkey

  • Safari → JavaScriptCore (Nitro)

Node.js uses Chrome’s V8 engine to execute JavaScript outside the browser.

Libuv

libuv is a multi-platform C library used inside Node.js. It provides important features such as:

  • Event loop

  • Asynchronous I/O

  • Thread pool

  • File system operations

  • Networking support

libuv is one of the main reasons Node.js can perform non-blocking asynchronous operations efficiently.

You can think of libuv as the component that helps Node.js interact with the operating system and manage asynchronous tasks behind the scenes.

C/C++ in Node.js

A large part of Node.js is written in C and C++ because operating systems cannot directly understand JavaScript code. Operating systems mainly work with low-level machine instructions.

The V8 engine itself is also written in C++. Node.js uses C/C++ code as a bridge between:

  • V8 Engine

  • Operating System

  • System-level APIs

This allows JavaScript code to interact with files, Network, Processes, Operating system features efficiently.

JavaScript Code
       ↓
     Node.js
 ┌─────────────┐
 │     V8      │ → Executes JS
 │    libuv    │ → Handles async operations
 │    C/C++    │ → Communicates with OS
 └─────────────┘
       ↓
Operating System

Difference Between JavaScript Running in Browser vs Node.js

Now, for example, if we write:

console.log("hello");

inside the browser console or run the same code in the Node.js environment, the output will be the same. Both will print:

hello

From the outside, the code looks exactly the same. However, internally, the mechanism used to execute the code is completely different.

In browsers, JavaScript code runs inside the browser’s JavaScript runtime environment. The browser provides features like:

  • DOM

  • Web APIs

  • window

  • document

along with its JavaScript engine.

In Node.js, JavaScript runs inside the Node.js runtime environment, which provides:

  • File system APIs

  • Networking APIs

  • Process handling

  • Event loop

  • libuv features

instead of browser APIs.

Although both environments execute JavaScript, they provide completely different features and capabilities.

The syntax of JavaScript remains mostly the same in both environments. That is why developers can write familiar JavaScript code in Node.js without learning an entirely new language.

How JavaScript Runs in Node.js

Now we are going to understand how JavaScript code actually runs behind the scenes in Node.js. Don’t worry — the concept is not as complex as it looks.

    +--------------------------------------------------+
    |              MAIN THREAD (JS)                    |
    +--------------------------------------------------+
    |     1. Initialize project                        |
    |     2. Execute top-level code                    |
    |     3. Register async callbacks                  |
    +--------------------------------------------------+
                     |
                     v
        +-----------------------------+
        |       EVENT LOOP START      |
        +-----------------------------+
                     |
                     v
        +------------------------------+
        |       while (work exists)    |
        +------------------------------+
                     |
                     v
        +-----------------------------+
        | 1. Timers                   |
        | (setTimeout, setInterval)   |
        +-----------------------------+
                     |
                     v
        +-----------------------------+
        | 2. Pending Callbacks        |
        +-----------------------------+
                     |
                     v
        +-----------------------------+
        | 3. Idle / Prepare           |
        | (internal)                  |
        +-----------------------------+
                     |
                     v
        +-----------------------------+
        | 4. Poll Phase               |
        | (I/O, fs, network)          |
        +-----------------------------+
                     |
                     v
        +-----------------------------+
        | 5. Check Phase              |
        | (setImmediate)              |
        +-----------------------------+
                     |
                     v
        +-----------------------------+
        | 6. Close Callbacks          |
        +-----------------------------+
                     |
                     v
        +-----------------------------+
        | Run Microtasks              |
        | (Promise, nextTick)         |
        +-----------------------------+
                     |
                     v
        +-----------------------------+
        |   Work remaining?           |
        +-----------------------------+
             | Yes           | No
             v               v
      (Loop continues)   (Process exits)

When a Node.js program starts, it first executes the top-level code. Top-level code means the code written outside functions and callbacks, such as:

  • Variable declarations

  • console.log()

  • Importing modules

  • Function definitions

Example :

console.log("Start");

const name = "Mehtab";

setTimeout(() => {
  console.log("Timer completed");
}, 1000);

console.log("End");

Node.js first executes all the synchronous top-level code line by line.

Registering Async Callbacks

When Node.js encounters asynchronous operations like:

  • setTimeout()

  • File system operations

  • Network requests

  • Database queries

it does NOT execute them immediately.

Instead, Node.js registers these asynchronous tasks and hands them over to libuv and the operating system for handling. libuv is responsible for managing asynchronous I/O operations and the event loop inside Node.js

For example, when you write:

setTimeout(() => {
  console.log("Hello");
}, 2000);

Node.js registers the timer with libuv. The timer countdown happens in the background while Node.js continues executing the remaining code. This is why asynchronous operations do not block the main thread.

The Main Thread

JavaScript in Node.js runs on a single main thread.

That main thread:

  • Executes JavaScript code

  • Handles the call stack

  • Runs callbacks

But asynchronous operations are handled separately using:

  • libuv

  • OS APIs

  • Thread pool (for some operations)

This is what allows Node.js to handle multiple tasks efficiently while still being single-threaded.

The Event Loop

After the synchronous code finishes execution, the event loop starts managing asynchronous callbacks.

The event loop continuously checks:

  • Are any timers completed?

  • Is any I/O operation finished?

  • Are any callbacks waiting to execute?

If callbacks are ready, the event loop pushes them into the call stack for execution.

This process keeps repeating until there is no more work left.

Event Loop Starts

We can think of the event loop as something similar to a while(true) loop that continuously keeps running until there are no more tasks left to process. The event loop continuously checks all phases from top to bottom and executes callbacks whenever they become ready. If no more tasks, timers, or callbacks remain, the Node.js process exits automatically.

Timers Phase

This phase checks whether any timers created using:

  • setTimeout()

  • setInterval()

have completed their delay time.

If a timer is ready, its callback is executed. Otherwise, the event loop moves to the next phase without waiting. Node.js never blocks the entire program while waiting for timers.

Pending Callbacks Phase

The pending callbacks phase executes certain callbacks that were deferred from the previous event loop iteration. This phase mainly handles :

  • Some system-level callbacks

  • Certain network errors

  • Deferred I/O callbacks

Again, Node.js does not stop and wait here. If no callbacks are pending, it immediately moves to the next phase.

Idle / Prepare Phase

This phase is used internally by Node.js for preparation purposes before moving to the next phase. Developers usually do not interact with this phase directly.

Poll Phase (I/O, File System, Network)

The poll phase is one of the most important phases of the event loop. It handles completed I/O operations such as :

  • File reading

  • Database operations

  • Network requests

  • Incoming data

Most asynchronous callbacks are executed here.

One important thing to remember :

The asynchronous operation itself usually starts earlier with the help of :

  • libuv

  • operating system APIs

  • thread pool

The poll phase mainly checks whether those operations have completed and whether their callbacks are ready to execute.

Check Phase (setImmediate)

This phase executes callbacks scheduled using setImmediate.

setImmediate(() => {
  console.log("Runs in check phase");
});

setImmediate is a Node.js-specific function that schedules a callback to execute immediately after the poll phase completes.

Close Callbacks Phase

This phase executes callbacks related to closed resources such as :

  • Sockets

  • Streams

  • Server connections

For example:

  • socket close events

  • cleanup operations

  • stream closing callbacks

are handled here.

Event Loop Continuation

After completing all phases, Node.js checks whether more work still exists. If tasks are still pending :

  • the event loop continues

Otherwise :

  • the Node.js process exits automatically

Conclusion

Node.js changed JavaScript from a browser-only language into a powerful backend technology. In this blog, we understood how Node.js works, how JavaScript runs outside the browser, and the role of the V8 engine, libuv, and the event loop. The biggest strength of Node.js is its non-blocking and event-driven architecture, which makes it fast, scalable, and ideal for modern backend applications.

Thank you for reading.