What is Node.js? Runtime, V8 & Architecture
Deep-dive introduction to Node.js: history by Ryan Dahl, the V8 engine and libuv, event-driven non-blocking I/O, Node.js vs browser JavaScript, real-world use cases, limitations, and exactly how Node.js executes your code under the hood.
Node.js is a free, open-source, cross-platform JavaScript runtime environment that lets you execute JavaScript code outside of a web browser. It was created in 2009 by Ryan Dahl, who wanted to build web servers capable of handling thousands of concurrent connections without the heavy memory overhead of one-thread-per-request models used by older servers like Apache.
Node.js is built on Google Chrome's open-source V8 JavaScript Engine โ the same high-performance engine that powers the Chrome browser. V8 compiles JavaScript directly to native machine code before executing it, which makes Node.js extremely fast for I/O-heavy workloads. Around this V8 core, Node.js adds its own C++ bindings and a library called libuv, which provides the event loop, thread pool, and non-blocking I/O operations (file access, networking, DNS, timers).
Why Node.js Was a Big Deal:
- One language, both sides: The same JavaScript you already know from the browser can now run your backend server, CLI tools, and build scripts.
- Non-blocking by default: Node.js handles thousands of simultaneous connections on a single thread using asynchronous, event-driven I/O.
- npm ecosystem: Node.js ships with npm, the largest software package registry in the world, giving instant access to millions of reusable packages.
- Backed by the OpenJS Foundation: Node.js is maintained by a large open-source community and used in production by Netflix, LinkedIn, PayPal, Uber, and NASA.
// Run this in your terminal, not inside a .js file
node --version
// Example output: v22.11.0
node --version: Asks the globally installed Node.js binary to print its own version number.- The
v22.11.0output confirms Node.js is installed correctly and shows which major version you're running (important, since APIs change between major versions).
JavaScript in the browser and JavaScript in Node.js share the exact same language (syntax, variables, functions, promises) because both are powered by the V8 engine. The difference is the environment around the language โ the set of built-in objects and APIs each platform exposes.
In the browser, JavaScript has access to the DOM (document, window) to manipulate web pages. Node.js has no browser, no DOM, and no window object โ instead, it exposes APIs for interacting with the operating system: the file system, network sockets, processes, and environment variables.
| Aspect | Browser JavaScript | Node.js |
|---|---|---|
| Global object | window | global / globalThis |
| DOM access | Yes (document, elements) | No DOM at all |
| File system access | Not allowed (sandboxed) | Full access via node:fs |
| Module system | ES Modules (<script type="module">) | CommonJS + ES Modules |
| Networking | fetch(), XHR (client only) | node:http โ can act as client and server |
| Typical use | UI interactivity, DOM updates | Servers, APIs, CLIs, build tools |
// node:process is a Node-only global โ the browser has no equivalent
console.log("Running on Node.js version:", process.version);
console.log("Current working directory:", process.cwd());
process: A global object available only in Node.js, giving information about โ and control over โ the current running program.process.version: Returns the exact Node.js version string powering this script.process.cwd(): Returns the absolute path of the directory the script was launched from.
A runtime environment is the software that provides everything a program needs to execute: memory management, a way to run instructions, and access to system resources. Node.js is JavaScript's runtime outside the browser โ it takes your .js file, hands it to V8 for execution, and wires up libuv underneath to talk to the operating system.
Node.js follows an event-driven, non-blocking I/O model. Instead of waiting (blocking) while a slow operation like reading a file or querying a database finishes, Node.js registers a callback function and immediately moves on to the next task. When the slow operation completes, Node's event loop picks up the result and runs your callback โ all on a single main thread.
console.log("1. Script starts");
// setTimeout is non-blocking โ Node registers it and moves on immediately
setTimeout(() => {
console.log("3. This runs later, after the main script finishes");
}, 0);
console.log("2. Script continues without waiting");
console.log("1. ..."): Executes immediately, synchronously.setTimeout(callback, 0): Even with a 0ms delay, Node hands this callback to libuv's timer queue and does not wait for it.console.log("2. ..."): Runs right after, before the timeout callback, because the main script must finish first.- Only once the call stack is empty does the event loop run the queued timeout callback, printing line
3last.
1. Script starts โ 2. Script continues without waiting โ 3. This runs later...Node.js is a general-purpose runtime, but its architecture makes it especially strong at specific kinds of work. Here's what it's commonly used for in production:
- REST & GraphQL APIs โ backend services for web and mobile apps, typically built with Express.js or Fastify.
- Real-time applications โ chat apps, live dashboards, and multiplayer features using WebSockets or Socket.IO.
- Microservices โ small, independently deployable services that communicate over HTTP or message queues.
- Command-line tools โ popular CLIs like npm itself, the Angular CLI, and Create React App are all built with Node.js.
- Build tooling โ bundlers like Webpack, Vite, and esbuild run on Node.js under the hood.
- Streaming services โ Node's stream APIs make it well suited for proxying and processing large amounts of data efficiently.
Node.js ships with npm (Node Package Manager) by default โ the world's largest software registry, with well over 2 million published packages covering everything from HTTP frameworks to date formatting utilities.
npm --version
npm init -y
npm install express
npm --version: Confirms npm ships alongside your Node.js installation.npm init -y: Generates a defaultpackage.jsonfile that describes your project and its dependencies.npm install express: Downloads the Express.js framework into a localnode_modulesfolder and records it as a dependency.
Node.js excels at I/O-bound work, but it is single-threaded for JavaScript execution. That means any long-running, CPU-intensive synchronous code (heavy image processing, complex calculations, large in-memory sorting) will block the single event loop thread โ freezing every other request your server is handling until that computation finishes.
node:worker_threads, a child process, or a different language/service entirely.
Common Limitations to Keep in Mind:
- CPU-bound tasks block the event loop and degrade performance for all connected clients.
- Callback complexity โ deeply nested callbacks ("callback hell") can hurt readability if Promises/async-await aren't used consistently.
- Immature ORMs (historically) โ improving fast, but some database tooling is younger than equivalents in Java or Python.
- Single-threaded by default โ to use multiple CPU cores you must explicitly use the
clustermodule or worker threads.
console.log("Server received request A");
// A heavy synchronous loop โ this blocks EVERYTHING until it finishes
let total = 0;
for (let i = 0; i < 5_000_000_000; i++) {
total += i;
}
console.log("Finished heavy computation:", total);
console.log("Only now can Server receive request B");
- The synchronous
forloop runs entirely on the main thread with no yielding. - While it runs, Node.js cannot process any other incoming requests, timers, or I/O callbacks โ everything queues up behind it.
- This is the single most common Node.js production mistake: putting heavy synchronous computation directly inside a request handler.
Understanding Node's internal architecture helps explain why it behaves the way it does. Node.js is composed of three main layers working together:
- V8 Engine: Parses and compiles your JavaScript into optimized machine code, and manages the JavaScript heap and garbage collection.
- libuv: A C library that provides the event loop, a thread pool for expensive operations (like file system access and DNS lookups), and cross-platform abstractions over the OS's async I/O primitives (epoll on Linux, kqueue on macOS, IOCP on Windows).
- Node.js Bindings & Core Modules: C++ bindings connect V8's JavaScript world to libuv's system-level world, exposing it all through familiar JavaScript APIs like
fs,http, andnet.
When your script calls an async function such as fs.readFile(), Node hands the actual file-reading work off to libuv's thread pool (or the OS kernel directly for networking), and immediately continues executing the rest of your script. The event loop continuously checks whether any pending operations have completed; once one finishes, its callback is pushed onto the call stack to run.
import { readFile } from "node:fs";
console.log("Start reading file...");
readFile("package.json", "utf8", (error, data) => {
if (error) {
console.error("Error:", error.message);
return;
}
console.log("File contents loaded:", data.length, "characters");
});
console.log("This line runs before the file finishes loading");
import { readFile }: Imports the async, callback-based file reading function from Node's built-innode:fsmodule.readFile(path, encoding, callback): Hands the actual disk read to libuv's thread pool and returns immediately โ it does not block.- The final
console.logruns before the file finishes loading, proving the call is non-blocking. - Once libuv finishes reading the file, the event loop schedules the callback, which then logs the file's length.
Q Is Node.js a programming language?
No. Node.js is a JavaScript runtime environment, not a language. The language is JavaScript; Node.js is the engine (V8) plus system-level APIs (libuv) that let that JavaScript run outside a browser.
Q Is Node.js single-threaded or multi-threaded?
Node.js executes your JavaScript on a single main thread, but libuv maintains a background thread pool for certain operations (like file I/O and DNS lookups), and you can spin up additional threads explicitly using the worker_threads module or the cluster module.
Q Can Node.js be used for the frontend?
Node.js itself runs on the server or command line, not inside a browser page. However, it's essential to frontend development anyway โ tools like Webpack, Vite, Babel, and npm all run on Node.js to build the JavaScript that eventually ships to the browser.