Node.js Overview
What is Node.js?
Node.js is a runtime environment that allows JavaScript to run outside the browser. Since Ryan Dahl introduced it in 2009, it has become the standard for server-side JavaScript.
Traditional web servers (Apache, Tomcat) occupy one thread per request. As concurrent users grow, threads scale linearly and memory/CPU are quickly exhausted. Node.js solves this problem with an event-driven, non-blocking I/O model.
V8 + libuv: The Two Engines of Node.js
┌─────────────────────────────────────┐
│ Node.js Runtime │
│ │
│ ┌──────────┐ ┌───────────────┐ │
│ │ V8 │ │ libuv │ │
│ │ (JS exec)│ │(async I/O) │ │
│ └──────────┘ └───────────────┘ │
│ │
│ ┌─────────────────────────────┐ │
│ │ Node.js Core API │ │
│ │ (fs, http, crypto, ...) │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────┘
V8 Engine
A high-performance JavaScript engine developed by Google for Chrome. It compiles JavaScript code to machine code via JIT (Just-In-Time) compilation. Node.js embeds V8 to execute JavaScript on the server.
libuv
A cross-platform asynchronous I/O library written in C. It handles the event loop, thread pool, file system, and networking. Node.js's non-blocking nature is mostly thanks to libuv.
Event-Driven Non-Blocking I/O Model
Blocking vs Non-Blocking
Blocking halts the entire thread while reading a file:
// Blocking code (synchronous)
const fs = require('fs');
const data = fs.readFileSync('/large-file.txt', 'utf8'); // Thread stops here
console.log('File read complete:', data.length, 'bytes');
console.log('This runs only after the above finishes');
Non-blocking hands the I/O off to the background and immediately continues:
// Non-blocking code (asynchronous)
const fs = require('fs');
fs.readFile('/large-file.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log('File read complete:', data.length, 'bytes');
});
console.log('This runs first!'); // Printed before file is read
The Event Loop
The core of Node.js. The mechanism for handling concurrency on a single thread:
┌──────────────────────────────────┐
│ Event Loop │
│ │
│ ┌──────────┐ │
│ │ timers │ setTimeout, setInterval
│ └─────┬────┘ │
│ │ │
│ ┌─────▼────────────┐ │
│ │ pending callbacks│ I/O errors │
│ └─────┬────────────┘ │
│ │ │
│ ┌─────▼──────┐ │
│ │ poll │ wait for I/O │
│ └─────┬──────┘ │
│ │ │
│ ┌─────▼──────┐ │
│ │ check │ setImmediate │
│ └─────┬──────┘ │
│ │ │
│ ┌─────▼─────────────┐ │
│ │ close callbacks │ │
│ └───────────────────┘ │
└──────────────────────────────────┘
// Verify event loop behavior
console.log('1: synchronous code');
setTimeout(() => console.log('3: setTimeout (0ms)'), 0);
Promise.resolve().then(() => console.log('2: Promise (microtask)'));
console.log('1-2: more synchronous code');
// Output order:
// 1: synchronous code
// 1-2: more synchronous code
// 2: Promise (microtask)
// 3: setTimeout (0ms)
The npm Ecosystem
npm (Node Package Manager) is the world's largest open-source package registry with over 2.3 million packages.
package.json Basics
# Initialize a new project
mkdir my-project && cd my-project
npm init -y
Generated package.json:
{
"name": "my-project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "jest"
},
"keywords": [],
"author": "",
"license": "ISC"
}
Installing Packages
# Install a regular dependency
npm install express
# Install a dev dependency
npm install --save-dev nodemon
# Install globally
npm install -g pm2
# Check package.json after installation
cat package.json
node_modules Structure
my-project/
├── node_modules/
│ ├── express/
│ ├── body-parser/
│ └── ...
├── package.json
└── package-lock.json
Always add to .gitignore:
node_modules/
Node.js vs Browser Differences
| Item | Node.js | Browser |
|---|---|---|
| Global object | global | window |
| DOM API | None | document, navigator, etc. |
| File system | Accessible via fs module | Inaccessible (security) |
| Module system | CommonJS(require) + ESM | ESM(import) |
| Execution env | Server / local machine | Sandboxed environment |
| Process mgmt | process object | None |
// Only works in Node.js
const os = require('os');
console.log('Platform:', process.platform);
console.log('Node version:', process.version);
console.log('CPU cores:', os.cpus().length);
console.log('Memory:', Math.round(os.totalmem() / 1024 / 1024 / 1024), 'GB');
// Global objects that don't exist in browsers
console.log(typeof window); // undefined (Node.js)
console.log(typeof global); // object (Node.js)
console.log(typeof process); // object (Node.js)
Module System Differences
// Node.js CommonJS (default)
const express = require('express');
module.exports = { myFunction };
// Node.js ESM (requires "type": "module" in package.json)
import express from 'express';
export { myFunction };
Node.js Version Management
Node.js releases even-numbered LTS versions and odd-numbered Current versions:
| Version | Type | Notes |
|---|---|---|
| 20.x | LTS (Active) | Long-term support, recommended for production |
| 22.x | LTS (Maintenance) | Security patches only |
| 23.x | Current | Latest features, experimental |
| 24.x | In development | Future version |
Managing Versions with nvm
# Install nvm (macOS/Linux)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
# Install a specific version
nvm install 20
nvm install 22
# Switch versions
nvm use 20
# Set default version
nvm alias default 20
# List installed versions
nvm ls
# Check current version
node --version
npm --version
Hands-On: Running Your First Node.js Server
Basic HTTP Server
// server.js
const http = require('http');
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('Hello from your Node.js server!\n');
});
server.listen(port, hostname, () => {
console.log(`Server running at: http://${hostname}:${port}/`);
});
node server.js
# Server running at: http://127.0.0.1:3000/
curl http://127.0.0.1:3000
# Hello from your Node.js server!
JSON API Server
// api-server.js
const http = require('http');
const users = [
{ id: 1, name: 'Alice', role: 'admin' },
{ id: 2, name: 'Bob', role: 'user' },
{ id: 3, name: 'Charlie', role: 'user' },
];
const server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'application/json; charset=utf-8');
if (req.url === '/users' && req.method === 'GET') {
res.statusCode = 200;
res.end(JSON.stringify({ success: true, data: users }));
} else if (req.url === '/health') {
res.statusCode = 200;
res.end(JSON.stringify({ status: 'ok', uptime: process.uptime() }));
} else {
res.statusCode = 404;
res.end(JSON.stringify({ success: false, message: '404 Not Found' }));
}
});
server.listen(3000, () => {
console.log('API Server: http://localhost:3000');
console.log('Endpoints: GET /users, GET /health');
});
Using the REPL
# Start Node.js REPL
node
# Inside the REPL
> const arr = [1, 2, 3, 4, 5]
> arr.filter(n => n % 2 === 0)
[ 2, 4 ]
> arr.reduce((acc, n) => acc + n, 0)
15
> .exit
Common Node.js Use Cases
When Node.js Shines
- Real-time applications: Chat, notifications, game servers — event-driven model excels here
- API servers: RESTful API, GraphQL — fast JSON processing
- Microservices: Lightweight, fast startup time
- Streaming services: Large file/media streaming
- CLI tools: npm, webpack, ESLint and most JS tooling is Node.js-based
When to Consider Alternatives
- CPU-intensive computation: Image/video processing, encryption, machine learning — Python/Go is more suitable
- Multi-threading needs: Single-threaded by default (solvable with worker_threads)
- Memory-intensive work: V8 heap memory limit (~1.5GB default)
Pro Tips
Using the process Object
// Print process information
console.log('Node version:', process.version);
console.log('Platform:', process.platform); // win32, linux, darwin
console.log('Architecture:', process.arch); // x64, arm64
console.log('PID:', process.pid);
console.log('Working directory:', process.cwd());
console.log('Executable path:', process.execPath);
// Environment variables
process.env.NODE_ENV = 'production';
console.log('Environment:', process.env.NODE_ENV);
// Process exit
process.on('exit', (code) => {
console.log(`Process exited with code: ${code}`);
});
// Clean exit
process.exit(0);
// Error exit
process.exit(1);
Measuring Performance
// Measure execution time
const { performance } = require('perf_hooks');
const start = performance.now();
let sum = 0;
for (let i = 0; i < 1_000_000; i++) {
sum += i;
}
const end = performance.now();
console.log(`Execution time: ${(end - start).toFixed(2)}ms`);
console.log(`Result: ${sum}`);
// console.time method
console.time('loop');
let total = 0;
for (let i = 0; i < 1_000_000; i++) total += i;
console.timeEnd('loop');
Monitoring Memory Usage
// Check memory usage
const used = process.memoryUsage();
console.log('Memory usage:');
for (const [key, value] of Object.entries(used)) {
console.log(` ${key}: ${Math.round(value / 1024 / 1024 * 100) / 100} MB`);
}
// heapUsed: heap memory currently in use
// heapTotal: total heap size
// rss: total memory allocated by the OS
// external: memory used by C++ bindings