An overview of Node.js, its architecture, ecosystem, and real-world use cases.
Table of Contents
- What Is Node.js?
- How Node.js Is Used
- Exploring Node.js in Practice
- Long-Term Support (LTS)
- Reference Tables
- Architecture Diagrams
1. What Is Node.js?
Definition and Positioning
Node.js is a standalone JavaScript runtime — an execution environment for JavaScript independent of the browser, installed directly on an operating system.
“Node.js is a standalone JavaScript runtime environment.”
Without Node.js, a .js file is just a text file. It can’t do anything on its own. Node.js provides the complete environment for JavaScript to interact with the OS, network, file system, etc.
Who uses Node.js?
| Company | Usage |
|---|---|
| Netflix | Streaming, user interface |
| Backend API | |
| PayPal | Web servers |
| Uber | Real-time services |
| Slack | Desktop application (Electron) |
| Trello | Web application |
| eBay | APIs and microservices |
| NASA | Real-time data management |
| Microsoft | VS Code (Electron/Node application) |
Node.js is used to build:
- Web applications and REST APIs
- Streaming applications
- Decentralized blockchain applications
- Real-time chat and collaboration tools
- Multiplayer games
- Microservices
- Serverless functions
- CLI (command line interfaces)
- Desktop applications (via Electron)
- IoT (Internet of Things) controllers
Node.js Architecture
Before Node.js, JavaScript was designed to run inside the browser. The browser is a complex application that includes:
- An HTML/CSS rendering engine
- A network manager
- A graphical interface
- A JavaScript engine (e.g., V8 in Chrome)
The browser’s JavaScript engine bridges JS code and OS resources (system clock, network, files, etc.). But JavaScript itself cannot directly access the OS — that would be a major security risk.
Node.js replaces the browser as the intermediary platform between JavaScript and the OS:
JavaScript Code
│
▼
┌─────────────┐
│ Node.js │ ← replaces the browser
│ ───────── │
│ V8 │ ← JavaScript engine (Google, open-source)
│ libuv │ ← task controller, event loop, async I/O
│ Bindings │ ← interface with OS (C/C++)
└─────────────┘
│
▼
Operating System (OS)
V8 Engine
V8 is Google’s open-source JavaScript engine, written in C++. It is used in Chrome and in Node.js.
- It is a JIT compiler (Just-In-Time): it compiles JavaScript into machine code at runtime
- It is extremely performant
- It handles garbage collection (memory)
- It executes JavaScript synchronously on a single thread
// V8 executes this JavaScript code
const message = "Hello from V8!";
console.log(message); // → "Hello from V8!"
libuv and the Event Loop
libuv is a C library that provides Node.js with:
- The event loop
- Asynchronous I/O management (files, network, DNS, etc.)
- A thread pool for blocking operations
- Timers (
setTimeout,setInterval) - Signals and child processes
How Does the Event Loop Work?
The event loop is the heart of Node.js. It’s what allows Node.js to be non-blocking despite a single JavaScript execution thread.
// Event loop demonstration
console.log("1 - Synchronous (start)");
setTimeout(() => {
console.log("3 - Timer callback (macrotask)");
}, 0);
Promise.resolve().then(() => {
console.log("2 - Promise callback (microtask)");
});
console.log("4 - Synchronous (end)");
// Output:
// 1 - Synchronous (start)
// 4 - Synchronous (end)
// 2 - Promise callback (microtask)
// 3 - Timer callback (macrotask)
Why this order? Synchronous code runs first. Microtasks (Promises,
queueMicrotask) take priority over macrotasks (setTimeout, setInterval, I/O callbacks).
Event Loop Phases
┌───────────────────────────┐
│ timers │ ← setTimeout, setInterval callbacks
└──────────┬────────────────┘
│
┌──────────▼────────────────┐
│ pending callbacks │ ← I/O errors from previous cycle
└──────────┬────────────────┘
│
┌──────────▼────────────────┐
│ idle, prepare │ ← internal Node.js use
└──────────┬────────────────┘
│
┌──────────▼────────────────┐
│ poll │ ← retrieve new I/O events
└──────────┬────────────────┘
│
┌──────────▼────────────────┐
│ check │ ← setImmediate callbacks
└──────────┬────────────────┘
│
┌──────────▼────────────────┐
│ close callbacks │ ← e.g.: socket.on('close', ...)
└───────────────────────────┘
Bindings API
Bindings are the C/C++ interfaces that allow JavaScript to communicate with the operating system. They enable:
- System clock access (
Date.now()) - File read/write
- TCP/UDP network connections
- External process execution
- Native module integration (
.nodefiles in C++)
2. How Node.js Is Used
The Module System
Node.js organizes code into modules. A module is a JavaScript file that encapsulates reusable code. Two module systems exist:
CommonJS vs ESM
| Characteristic | CommonJS (CJS) | ES Modules (ESM) |
|---|---|---|
| Import syntax | require() | import |
| Export syntax | module.exports | export / export default |
| Loading | Synchronous | Asynchronous |
| File extension | .js (default) | .mjs or "type": "module" |
| Specification | 2009 (Node.js) | ES2015 (official ECMAScript) |
| Browser support | No (native) | Yes |
Top-level await | No | Yes |
| Tree-shaking | Difficult | Possible |
// ─── CommonJS (CJS) ───────────────────────────────────────────
// math.js
function add(a, b) { return a + b; }
module.exports = { add };
// index.js
const { add } = require('./math');
console.log(add(2, 3)); // 5
// ─── ES Modules (ESM) ─────────────────────────────────────────
// math.mjs (or package.json with "type": "module")
export function add(a, b) { return a + b; }
// index.mjs
import { add } from './math.mjs';
console.log(add(2, 3)); // 5
Best practice: For new projects, prefer ESM (
"type": "module"inpackage.json). It’s the modern standard aligned with browsers.
Essential Built-in Modules
Node.js ships with a rich standard library:
// HTTP Server
import { createServer } from 'http';
const server = createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello, World!\n');
});
server.listen(3000, () => {
console.log('Server running at http://localhost:3000/');
});
// Streams - processing large data in chunks
import { createReadStream, createWriteStream } from 'fs';
import { pipeline } from 'stream/promises';
import { createGzip } from 'zlib';
// Compress a file without loading everything into memory
await pipeline(
createReadStream('input.txt'),
createGzip(),
createWriteStream('output.txt.gz')
);
console.log('Compression complete!');
// Events - EventEmitter
import { EventEmitter } from 'events';
const emitter = new EventEmitter();
// Listen for an event
emitter.on('data', (payload) => {
console.log('Data received:', payload);
});
// Emit an event
emitter.emit('data', { user: 'Alice', action: 'login' });
// → Data received: { user: 'Alice', action: 'login' }
// File System (fs) - asynchronous reading
import { readFile, writeFile } from 'fs/promises';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const filePath = join(__dirname, 'data.json');
const content = await readFile(filePath, 'utf-8');
const data = JSON.parse(content);
data.updatedAt = new Date().toISOString();
await writeFile(filePath, JSON.stringify(data, null, 2));
console.log('File updated');
// Path - cross-platform path manipulation
import path from 'path';
const fullPath = path.join('/users', 'alice', 'documents', 'file.txt');
console.log(fullPath); // /users/alice/documents/file.txt
console.log(path.extname(fullPath)); // .txt
console.log(path.basename(fullPath)); // file.txt
console.log(path.dirname(fullPath)); // /users/alice/documents
// Built-in test runner (stable since Node.js 20)
import { test, describe } from 'node:test';
import assert from 'node:assert/strict';
describe('My add() function', () => {
test('returns the sum of two numbers', () => {
assert.equal(2 + 3, 5);
});
test('handles negative numbers', () => {
assert.equal(-1 + 1, 0);
});
});
npm and Package Management
npm (Node Package Manager) is Node.js’s official package manager. It installs automatically with Node.js.
It provides:
- A CLI tool for managing project dependencies
- A public registry with over 2 million open-source packages
Initialize a Project
# Create a new project (interactive)
npm init
# Create a new project (automatic answers)
npm init -y
# Set ESM module type
npm pkg set type=module
Manage Dependencies
# Install a package (production dependency)
npm install express
# Install a package (development dependency)
npm install --save-dev jest
# Install all dependencies of an existing project
npm install
# Update packages
npm update
# Uninstall a package
npm uninstall express
# List installed packages
npm list
Example package.json
{
"name": "my-node-app",
"version": "1.0.0",
"description": "A Node.js application",
"type": "module",
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "node --watch index.js",
"test": "node --test"
},
"dependencies": {
"express": "^5.1.0"
},
"devDependencies": {
"eslint": "^9.0.0"
}
}
The caret
^before the version means: accept this major version (5) and all future minor/patch updates (5.x.x), but not the next major version (6.x.x).
Alternative Package Managers
| Tool | Characteristics |
|---|---|
| npm | Official, bundled with Node.js |
| Yarn | Faster, deterministic lockfile, workspaces |
| pnpm | Very disk space-efficient, symlinks |
| Bun | Ultra-fast runtime + package manager (Node alternative) |
The Node.js Ecosystem
The Node.js ecosystem goes far beyond the runtime itself. The community has produced thousands of frameworks, libraries, and tools.
Web Frameworks
| Framework | Description | Type |
|---|---|---|
| Express | Minimal and flexible framework, most popular | Server-side |
| Fastify | Express alternative, very performant | Server-side |
| Koa | Created by the Express team, more modern | Server-side |
| Hapi | Robust framework for enterprise APIs | Server-side |
| NestJS | TypeScript framework with architecture focus | Server-side |
| Next.js | React + SSR + API routes | Full-stack |
| Nuxt.js | Vue.js + SSR + API routes | Full-stack |
| Remix | Full-stack React with focus on web standards | Full-stack |
Databases
| Category | Popular Tools |
|---|---|
| ORM/ODM | Prisma, TypeORM, Mongoose, Sequelize, Drizzle |
| SQL | pg (PostgreSQL), mysql2, better-sqlite3 |
| NoSQL | MongoDB (via Mongoose), Redis (ioredis) |
| Migrations | Knex.js, db-migrate |
Testing
| Tool | Type |
|---|---|
| Jest | Full-featured test framework (unit, integration) |
| Vitest | Jest alternative, Vite-compatible |
| Mocha | Flexible test framework |
| Chai | Assertions library |
| Supertest | HTTP API testing |
| Playwright | Browser end-to-end testing |
| node:test | Built-in test runner (stable since v20) |
Build Tools and Bundlers
| Tool | Usage |
|---|---|
| webpack | Established bundler, highly configurable |
| Vite | Modern bundler, ultra-fast |
| esbuild | Ultra-fast bundler (Go) |
| Rollup | Library bundler |
| Parcel | Zero-config bundler |
| tsc | TypeScript compiler |
Code Quality Tools
| Tool | Usage |
|---|---|
| ESLint | JavaScript/TypeScript linter |
| Prettier | Code formatting |
| Husky | Git hooks |
| lint-staged | Linting before commit |
3. Exploring Node.js in Practice
First Node.js Project
# 1. Check installed versions
node -v # e.g.: v22.11.0
npm -v # e.g.: 10.9.0
# 2. Initialize the project
npm init -y
# 3. Configure ESM
npm pkg set type=module
# 4. Create the main file
touch index.js
// index.js - Hello World
console.log("Hello, World!");
# 5. Run with Node
node index.js
# → Hello, World!
Complete Example: HTTP Server with Routing
// server.js
import { createServer } from 'http';
const PORT = process.env.PORT || 3000;
const routes = {
'GET /': (res) => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ message: 'Welcome to my Node.js API!' }));
},
'GET /health': (res) => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ status: 'ok', uptime: process.uptime() }));
}
};
const server = createServer((req, res) => {
const key = `${req.method} ${req.url}`;
const handler = routes[key];
if (handler) {
handler(res);
} else {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Route not found' }));
}
});
server.listen(PORT, () => {
console.log(`Server started at http://localhost:${PORT}`);
});
Real Questions About Node.js
Q: Your team needs a backend for a chat app handling many simultaneous lightweight connections. Is Node.js a good choice?
Yes. Node.js handles concurrent lightweight connections very well thanks to its asynchronous, single-threaded, event-driven model. It doesn’t block the main thread waiting for network or database responses. This is exactly the use case for which Node.js excels.
Q: You’re building an ML pipeline training models and performing large numerical computations. Is Node.js the best choice?
Generally, no. Heavy computations would block the main thread. Python is a better primary choice (NumPy, TensorFlow, PyTorch). However, Node.js can be useful for serving these models via an API or coordinating lightweight tasks.
Q: “Single-threaded means Node.js can’t do parallel work.” Is this accurate?
Not entirely. There’s only one process stack — two JavaScript instructions don’t execute at exactly the same time, that’s the event loop. But Node.js can still do real parallel work via:
child_process(child processes)worker_threads(worker threads)- I/O operations handled in parallel by the OS via libuv
Q: A simple static site needs to serve files. Should you build a Node.js server?
No, use something else. A CDN or static host (Netlify, Vercel, S3, etc.) is simpler, cheaper, and scales automatically. Building your own Node.js server would be over-engineering for this case.
Q: Which Node.js version to choose for production?
Always an LTS (Long-Term Support) version with an even number (e.g., 20, 22, 24). Odd versions (21, 23, 25) are “Current” experimental versions preparing the next LTS.
4. Long-Term Support (LTS)
Node.js follows a well-defined release cycle with semantic versioning (SemVer): MAJOR.MINOR.PATCH.
Version Lifecycle
Version published
│
▼
Current (6 months)
← active development, new features ─►
│
▼ (for even versions only)
Active LTS (18 months)
← bug fixes, security ─►
│
▼
Maintenance LTS (12 months)
← critical fixes and security only ─►
│
▼
End of Life (EOL)
← no more support ─►
Version Table (example)
| Version | Status | Note |
|---|---|---|
| v25.x | Current | Experimental, odd number |
| v24.x | Active LTS | Recommended for production |
| v22.x | Active LTS | Previous LTS |
| v20.x | Maintenance LTS | Nearing end of support |
| v18.x | EOL | Do not use |
Golden rule: In production, use an active LTS version (even number). Avoid EOL versions that no longer receive security patches.
Node.js is open-source, community-managed via GitHub, and financially supported by the OpenJS Foundation.
5. Reference Tables
Node.js Built-in Modules
| Module | Description | Typical Usage |
|---|---|---|
http / https | HTTP/HTTPS server and client | Create web servers, make requests |
fs / fs/promises | File system operations | Read/write files, manage directories |
path | Path manipulation (cross-platform) | Join, resolve, extract file paths |
events | EventEmitter pattern | Pub/sub, custom events |
stream | Streaming API | Process large data in chunks |
buffer | Binary data manipulation | Encode/decode data |
url | URL parsing | Parse and build URLs |
crypto | Cryptographic functions | Hashing, encryption, tokens |
os | OS information | CPU count, memory, platform |
process | Current process control | Environment variables, exit, signals |
child_process | Run external processes | Shell scripts, other programs |
worker_threads | Real multithreading | CPU-intensive parallel tasks |
net / dgram | Low-level TCP/UDP | Sockets, custom protocols |
dns | Domain resolution | Resolve hostnames |
zlib | Compression (gzip, deflate) | Compress/decompress data |
assert | Test assertions | Unit tests |
node:test | Built-in test runner | Unit and integration tests |
cluster | Multi-process clustering | Scale across CPU cores |
readline | CLI input | Interactive command-line tools |
querystring | URL parameter parsing | Parse query strings |
Comparison with Other Runtimes
| Aspect | Node.js | Deno | Bun |
|---|---|---|---|
| Creator | Ryan Dahl (2009) | Ryan Dahl (2020) | Jarred Sumner (2022) |
| Engine | V8 | V8 | JavaScriptCore |
| Security | Optional | Permissions by default | Permissions by default |
| TypeScript | Via tsc/transpiler | Native | Native |
| Module system | CJS + ESM | ESM (CJS via compat.) | CJS + ESM |
| Bundler | External | Built-in | Built-in |
| npm compatible | Yes | Partial | Yes |
| Maturity | Very high | Growing | Recent |
| Use cases | All | Modern projects | Speed-critical |
6. Architecture Diagrams
Node.js vs Browser Architecture
graph LR
subgraph "Before Node.js: Browser"
JS1[JavaScript] --> BROWSER[Browser Engine]
BROWSER --> RENDER[Rendering Engine]
BROWSER --> V8B[V8 JavaScript Engine]
BROWSER --> NET1[Network Module]
BROWSER --> DOM[DOM API]
BROWSER --> OS1[Operating System]
end
subgraph "With Node.js: Server"
JS2[JavaScript] --> NODE[Node.js Runtime]
NODE --> V8N[V8 Engine]
NODE --> LIBUV[libuv\nEvent Loop]
NODE --> BINDN[C++ Bindings]
NODE --> OS2[Operating System]
end
style BROWSER fill:#dbeafe
style NODE fill:#dcfce7
npm Ecosystem
flowchart TD
DEV[Developer] -->|npm install| NPM_CLI[npm CLI]
NPM_CLI -->|downloads| REGISTRY["npm Registry\n(registry.npmjs.org)\n2M+ packages"]
NPM_CLI -->|creates| NM[node_modules/]
NPM_CLI -->|updates| PKG[package.json\npackage-lock.json]
subgraph "Alternative CLIs"
YARN[Yarn]
PNPM[pnpm]
BUN[Bun]
end
YARN -->|accesses| REGISTRY
PNPM -->|accesses| REGISTRY
BUN -->|accesses| REGISTRY
subgraph "Package Types"
FRAME[Frameworks\nExpress, Fastify, NestJS]
TEST[Testing\nJest, Vitest, Mocha]
BUILD[Build Tools\nVite, esbuild, webpack]
UTIL[Utilities\nlodash, dayjs, zod]
end
REGISTRY --- FRAME
REGISTRY --- TEST
REGISTRY --- BUILD
REGISTRY --- UTIL
Search Terms
node.js · apis · microservices · backend · full-stack · web · architecture · event · loop · built-in · ecosystem · npm · package · tools · version