Node.js: JavaScript on the Server Explained

Hello readers, in this blog you would learn the high level architecture of node.js and understand how it is able to handle things so smoothly and efficiently.
What is Node.js ? (JavaScript Beyond the Browser)
Before we answer the heading itself, you know JavaScript is a scripting language, which was originally designed to be run on the web browser to make web pages interactive and dynamic. It was primarily designed to handle user interactions on the frontend.
Ryan Dhal, got this idea of taking javascript and running it on a server and created node.js which would be able of scalable backend system, handling file operation and network applications using the same language.
Node.js is nothing but a runtime environment for JavaScript.
Why JavaScript was originally browser-only ?
In the earlier days, when javascript was initially created, the web consisted of only static HTML and had minimal interactivity, everything was happening inside the browser. There was no concept of JS as a general purpose language yet. Javascript was introduced as a browser scripting language.
JavaScript allowed developers to handle user interactions such as button clicks, form validation, and dynamic content updates directly within the browser. Since these interactions happen on the user interface, it made sense for JavaScript to run inside the browser itself.
Since JS was developed to run on the browser only to add interactivity to the HTML pages, it did not have access to read/write files of the user system to prevent JavaScript from accessing sensitive resources like the file system.
Making JS run on servers
Node.js brought Javascript from browsers to servers. JavaScript which was confined to handle frontend interactions was now ready to be a backend deal.
Now node.js achieved this by combining multiple components into a unified runtime environment. Node.js used v8 engine at it's core, which executes JS code and manages the call stack for synchronous operations.
However this was not enough because running JS on a server requires executing tasks like file handling, networking, and timers which require interactions with the operating system. To achieve this Node.js uses bindings (written in CPP) to connect JS with lower level system operations.
These operations are handled by libuv, a C-based library that manages asynchronous tasks using an event-driven architecture. libuv interacts directly with the operating system and ensures that long-running operations do not block the main thread.
Once an asynchronous task is completed, its callback is placed in a queue, and the event loop ensures it is executed by V8 when the call stack is free.
fs.readFile("file.txt", cb);
The above code is executed in the following way:
code runs in V8
fs module is not present in V8 engine
fs is recognized as a Node.js core module
Node takes over
Node bindings receive it and provides it to libuv
libub handles the operation and interacts with OS
Callback → queue → event loop → back to V8 Engine
Step | Entity | Role |
Call | V8 Engine | Identifies the function call and hits the Node API. |
Bridge | C++ Bindings | Converts the JS request into a C++ command. |
Work | libuv | Assigns the task to a thread so the main thread doesn't block. |
OS | File System | Physically retrieves the bits from the disk. |
Return | Event Loop | Picks up the finished task and pushes the callback back to V8. |
V8 Engine overview
The V8 Engine is an open-source, high-performance JavaScript and WebAssembly engine developed by Google. Written in C++, it compiles JavaScript directly into machine code for fast execution.
V8 transforms JavaScript from human-readable source into optimized machine code through a multi-stage pipeline. The parser builds an Abstract Syntax Tree, which is converted by the Ignition interpreter into bytecode for immediate execution. Hot code paths identified at runtime are passed to TurboFan, V8’s optimizing compiler, which recompiles them into highly efficient machine code. This just-in-time (JIT) approach balances startup speed and long-term performance.
Memory Management in V8
V8 employs a generational garbage collector that divides memory into young and old regions. Short-lived objects are reclaimed quickly, while long-lived ones are handled less frequently. Mark-and-sweep and reference-counting techniques ensure minimal pause times, providing responsive performance for browsers and servers alike.
Optimisations Techniques used in V8
Performance is further enhanced through inline caches, which remember object property access patterns to speed up subsequent lookups.
Important parts of V8 Engine:
Call Stack
Memory Heap
JIT Compilation
Event-driven architecture idea
Node.js follows an event driven architecture, which means that the flow of execution is determined by events rather than a fixed sequence of instructions.
Event is a signal that something has happened in the system like a timer completing, a file being read, or a request being received.
Operations like timers, file reads or network requests are initiated and delegated to the system. Instead of waiting for this tasks to complete, Node.js registers callback functions to be executed when the event occurs.
Once the operation is finished, the result triggers an event, and the associated callback is placed in the queue. The event loop which is provided by the libuv which continuously keeps on running, checks if the call stack is free, if it finds the call stack to be free, it picks up the callback from the queue which was earlier pushed as a triggered event.
This approach allows Node.js to handle multiple operations efficiently without blocking execution.
Node.js with traditional backend runtimes like PHP or Java
Node.js is not the single player in this game, it competes with backend runtimes like PHP or Java.
PHP/ Java are traditional backend runtimes and follow blocking, thread-based model. In this approach, each incoming request is handled by a separate thread or process. When a task such as file reading or a database query is performed, the thread handling that request remains blocked until the operation completes.
Such architecture design follows concurrent requests execution and could become resource intensive. As the number of requests increases, more threads are required, leading to higher memory usage and context-switching overhead.
Analogy: (PHP/Java) A full-service restaurant where each table has its own dedicated waiter. If the waiter is waiting for the chef, they can't help any other tables.
Node.js on the other hand follows a non-blocking, event driven architecture. Instead of assigning thread per request, Node.js uses a single thread execution model combined with asynchronous operations handled by libuv.
Analogy (Node.js): A single fast-food cashier taking orders and handing out buzzers. They don't wait for the food to cook; they keep taking orders while the kitchen works in the background.
NOTE: It is a common misconception that Node.js is "faster" than Java. In terms of raw calculation and execution of complex logic, Java’s JIT compiler often outperforms V8. Node.js is "faster" specifically at handling a high volume of network and disk I/O tasks with minimal resources.
Real World use cases of Node.js
Because Node.js excels at handling many simultaneous connections with low overhead, it has become the gold standard for specific types of modern applications.
Here are the most common real-world use cases where Node.js is the preferred choice:
Real- Time Applications: This is node's "bread & butter". Because of web-sockets, node can maintain an open connection between the client and server with almost no lag.
Streaming services: A core feature where it can process data chunks as they arrive rather than waiting for the entire file to download.
Micro-services Architecture: Large companies often break their "monolith" apps into hundreds of tiny, specialised services. Node’s lightweight nature makes it perfect for these small, fast-booting pieces.
Internet of Things: Node can handle IoT devices small payload requests which are in million tiny requests on low-resource hardware like a Raspberry Pi.
Conclusion
Node.js fundamentally changed how JS is used by taking it out of the browser and putting it on servers. It is amazing to realise that a language for adding interactivity to web pages evolved into a powerful tool for building scalable backend systems.
Making node.js use a non-blocking, event driven architecture was another smart move which differentiated it from other traditional backend services.
Understanding how Node.js works under the hood not only helps to write better code but the confidence to explain why a particular code is behaving that way in technical discussions.
Now in the upcoming blogs, we would discuss in greater details about the event loop functioning.


