# Node.js — User Guide

Node.js is a JavaScript runtime built on Chrome's V8 engine that executes JavaScript outside the browser, enabling server-side applications, command-line tools, and general-purpose scripting. It uses an event-driven, non-blocking I/O model that makes it lightweight and efficient for data-intensive real-time applications. With over 12,000 source files and a comprehensive standard library, Node.js provides everything needed to build production systems without third-party dependencies for core functionality.

---

## 1. Quick Start

### 1.1 What You Get with Node.js

- **Full-featured JavaScript runtime** — run JavaScript anywhere, not just in browsers
- **Comprehensive standard library** — HTTP/HTTPS servers, file system, crypto, streams, DNS, and 30+ built-in modules
- **Two module systems** — CommonJS (`require`) and ES Modules (`import`) with full interoperability
- **Built-in test runner** — `node:test` module with assertions, mocking, coverage, and reporters
- **TypeScript support** — native type stripping via `--experimental-strip-types` (powered by Amaro)
- **Permission model** — fine-grained control over file system, child process, and worker access
- **Watch mode** — automatic restart on file changes with `--watch`
- **Modern web APIs** — `fetch()`, `WebSocket`, `Blob`, `FormData`, `structuredClone`, and more built in

> [!TIP]
> If you already have Node.js installed, jump straight to the HTTP server example below. For version management across projects, install `nvm` or `fnm` first — it saves significant headaches down the road.

### 1.2 Prerequisites

| Requirement | Minimum | Recommended |
|-------------|---------|-------------|
| Operating system | macOS 11+, Windows 10+, Linux (glibc 2.28+) | Latest stable release |
| Disk space | 100 MB | 500 MB (with npm cache) |
| RAM | 512 MB | 2 GB+ for development |
| Architecture | x64, ARM64 | x64 or Apple Silicon |

### 1.3 Install in One Command

**macOS / Linux:**

```bash
curl -fsSL https://fnm.vercel.app/install | bash && fnm install --lts
```

**Windows (PowerShell):**

```powershell
winget install Schniz.fnm
fnm install --lts
```

### 1.4 Hello World

Create `hello.js`:

```javascript
console.log("Hello from Node.js", process.version);
```

Run it:

```bash
node hello.js
```

Expected output:

```text
Hello from Node.js v22.14.0
```

### 1.5 Hello World HTTP Server

Create `server.js`:

```javascript
const http = require("node:http");

const server = http.createServer((req, res) => {
  res.writeHead(200, { "Content-Type": "text/plain" });
  res.end("Hello, World!\n");
});

server.listen(3000, "127.0.0.1", () => {
  console.log("Server running at http://127.0.0.1:3000/");
});
```

Run it:

```bash
node server.js
```

Expected output:

```text
Server running at http://127.0.0.1:3000/
```

Test it:

```bash
curl http://127.0.0.1:3000/
```

```text
Hello, World!
```

> [!TIP]
> During development, use `node --watch server.js` to automatically restart the server when you edit the file. No external tools like `nodemon` required.

---

## 2. Installation

### 2.1 System Requirements

| Platform | Architectures | Notes |
|----------|--------------|-------|
| macOS 11+ (Big Sur) | x64, ARM64 (Apple Silicon) | Xcode Command Line Tools recommended for native addons |
| Windows 10+ | x64, ARM64 | Visual Studio Build Tools for native addons |
| Ubuntu 20.04+ | x64, ARM64, ARMv7 | `build-essential` for native addons |
| Debian 10+ | x64, ARM64 | `build-essential` for native addons |
| RHEL / CentOS 8+ | x64, ARM64 | `gcc-c++` and `make` for native addons |
| Alpine 3.15+ | x64, ARM64 | Uses musl libc; some packages need recompilation |
| Docker | Any host | Official images at `docker.io/library/node` |

### 2.2 Platform-Specific Installation

#### macOS

**Homebrew (recommended):**

```bash
brew install node
```

**Official installer:**

Download the `.pkg` from [nodejs.org](https://nodejs.org) and run it.

**Verify:**

```bash
node --version
npm --version
```

```text
v22.14.0
10.9.2
```

#### Linux

**Ubuntu / Debian:**

```bash
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejs
```

**RHEL / Fedora / CentOS:**

```bash
curl -fsSL https://rpm.nodesource.com/setup_22.x | sudo bash -
sudo dnf install -y nodejs
```

**Alpine:**

```bash
apk add nodejs npm
```

> [!IMPORTANT]
> Distribution-provided Node.js packages are often severely outdated. Use NodeSource, `fnm`, or `nvm` to get current versions. Running an unsupported Node.js version means no security patches.

#### Windows

**winget (Windows 10+):**

```powershell
winget install OpenJS.NodeJS.LTS
```

**Chocolatey:**

```powershell
choco install nodejs-lts
```

**Official installer:**

Download the `.msi` from [nodejs.org](https://nodejs.org). The installer adds `node` and `npm` to your PATH automatically.

> [!WARNING]
> On Windows, building native addons (packages with C++ bindings) requires the "Desktop development with C++" workload from Visual Studio Build Tools. Install it with: `npm install --global windows-build-tools` or download Visual Studio Build Tools directly.

#### Docker

```bash
docker run -it --rm node:22-alpine node -e "console.log('Node', process.version)"
```

**Dockerfile for production:**

```dockerfile
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .

FROM node:22-alpine
WORKDIR /app
COPY --from=builder /app .
USER node
EXPOSE 3000
CMD ["node", "server.js"]
```

### 2.3 Version Managers

Version managers let you switch Node.js versions per project. Two popular choices:

#### fnm (Fast Node Manager)

```bash
# Install fnm
curl -fsSL https://fnm.vercel.app/install | bash

# Install a specific version
fnm install 22
fnm install 20

# Use a version
fnm use 22

# Set default
fnm default 22

# Auto-switch per directory (.node-version or .nvmrc)
fnm env --use-on-cd
```

#### nvm (Node Version Manager)

```bash
# Install nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash

# Restart terminal, then:
nvm install --lts
nvm install 22
nvm install 20

# Switch versions
nvm use 22

# Set default
nvm alias default 22

# Use .nvmrc file
echo "22" > .nvmrc
nvm use
```

**Create a `.node-version` file in your project root:**

```bash
echo "22" > .node-version
```

Both `fnm` and `nvm` will detect this file and switch automatically (with shell integration enabled).

### 2.4 Building from Source

Building from source is necessary when targeting unsupported platforms, enabling custom build flags, or debugging Node.js internals.

**Prerequisites:**

| Dependency | Minimum Version |
|-----------|----------------|
| Python | 3.12+ |
| GCC / Clang | GCC 10.1+ or Clang 8+ |
| GNU Make | 3.81+ |
| Git | any recent |

**Build steps:**

```bash
git clone https://github.com/nodejs/node.git
cd node
git checkout v22.14.0

# Configure (see below for flags)
./configure

# Build (use -j for parallel compilation)
make -j$(nproc)

# Test (optional)
make test

# Install
sudo make install
```

**Common configure.py flags:**

| Flag | Purpose |
|------|---------|
| `--prefix=/usr/local` | Installation directory |
| `--debug` | Build with debug symbols |
| `--without-npm` | Skip bundling npm |
| `--shared-openssl` | Link against system OpenSSL |
| `--shared-zlib` | Link against system zlib |
| `--shared-libuv` | Link against system libuv |
| `--with-intl=full-icu` | Full internationalization support |
| `--with-intl=small-icu` | English-only ICU (smaller binary) |
| `--without-intl` | No internationalization |
| `--openssl-use-def-ca-store` | Use system certificate store |
| `--ninja` | Use Ninja build system (faster) |
| `--enable-lto` | Enable Link-Time Optimization |
| `--fully-static` | Fully static binary |
| `--without-inspector` | Disable inspector/debugger protocol |
| `--without-dtrace` | Disable DTrace probes |
| `--without-etw` | Disable Event Tracing for Windows |
| `--v8-lite-mode` | V8 lite mode (less memory, slower JIT) |
| `--without-node-options` | Disable NODE_OPTIONS env var |
| `--without-ssl` | Build without TLS support |
| `--with-arm-float-abi` | ARM floating point ABI (hard/soft/softfp) |
| `--with-arm-fpu` | ARM FPU type (vfp, vfpv3, neon) |
| `--cross-compiling` | Enable cross-compilation |
| `--dest-cpu` | Target CPU architecture |
| `--dest-os` | Target operating system |

> [!NOTE]
> The full list of configure flags is available via `./configure --help`. For architecture details, security hardening options, and advanced build configurations, see the Admin Guide.

### 2.5 Post-Install Verification

Run these commands to confirm everything works:

```bash
# Check versions
node --version
npm --version

# Test JavaScript execution
node -e "console.log('Node.js works!')"

# Test module loading
node -e "const os = require('node:os'); console.log(os.platform(), os.arch())"

# Test npm
npm --version
npm init -y
```

Expected output pattern:

```text
v22.14.0
10.9.2
Node.js works!
linux x64
```

### 2.6 Installation Troubleshooting

#### `node: command not found`

**Cause:** Node.js is not in your system PATH.

**Solution:**

1. If installed via nvm/fnm: run `nvm use default` or `fnm use default`
2. If installed from package: check `/usr/local/bin/node` exists
3. Add to PATH manually: `export PATH="/usr/local/bin:$PATH"` in your shell profile

#### `npm ERR! EACCES permission denied`

**Cause:** npm is trying to write to a global directory owned by root.

**Solution:**

1. Use a version manager (nvm/fnm) — this avoids the problem entirely
2. Or configure npm to use your home directory: `npm config set prefix '~/.npm-global'` and add `~/.npm-global/bin` to PATH

#### `gyp ERR! stack Error: Could not find any Python installation`

**Cause:** Native addon compilation requires Python.

**Solution:**

```bash
# macOS
xcode-select --install

# Ubuntu/Debian
sudo apt install python3 build-essential

# Windows
npm install --global windows-build-tools
```

#### `Error: Cannot find module 'node:...'`

**Cause:** You are running a Node.js version too old for the `node:` prefix (added in v16.0.0).

**Solution:** Upgrade to Node.js 16+ or remove the `node:` prefix from imports.

#### `N-API version mismatch`

**Cause:** Native addon was compiled for a different Node.js version.

**Solution:**

```bash
npm rebuild
# or
rm -rf node_modules && npm install
```

#### `Error: ENOSPC: System limit for number of file watchers reached`

**Cause:** Linux inotify watcher limit is too low (common with large projects or `--watch` mode).

**Solution:**

```bash
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
```

#### `GLIBC_2.28 not found`

**Cause:** Your Linux distribution has an older glibc than Node.js requires.

**Solution:** Upgrade your distribution or use the Alpine/musl build, or build Node.js from source.

#### `ERR_OSSL_EVP_UNSUPPORTED`

**Cause:** OpenSSL 3.0 dropped support for some legacy algorithms. Common when upgrading to Node.js 17+.

**Solution:**

```bash
# Temporary workaround (not recommended for production)
node --openssl-legacy-provider your-script.js

# Permanent fix: update the affected package
npm update
```

#### `Digital envelope routines::unsupported`

**Cause:** Same root cause as above — OpenSSL 3.0 incompatibility with older packages using MD4/MD5 for hashing.

**Solution:**

```bash
export NODE_OPTIONS=--openssl-legacy-provider
```

#### `MODULE_NOT_FOUND` after `nvm use`

**Cause:** Global packages were installed under a different Node.js version.

**Solution:**

```bash
nvm install <version> --reinstall-packages-from=<old-version>
```

#### Multiple Node.js installations conflicting

**Cause:** System package manager and version manager both installed Node.js.

**Solution:**

```bash
# Find all node binaries
which -a node

# Remove system version
sudo apt remove nodejs  # Debian/Ubuntu
brew uninstall node     # macOS

# Use only the version manager
nvm use default
```

---

## 3. Configuration

### 3.1 Environment Variables

Node.js behavior is controlled through environment variables. These are the most commonly used:

| Variable | Purpose | Example |
|----------|---------|---------|
| `NODE_ENV` | Application environment mode | `production`, `development`, `test` |
| `NODE_OPTIONS` | Pass CLI flags via environment | `--max-old-space-size=4096` |
| `NODE_PATH` | Additional module search paths | `/usr/local/lib/node_modules` |
| `NODE_DEBUG` | Enable debug output for core modules | `http,net,tls` |
| `NODE_TLS_REJECT_UNAUTHORIZED` | Disable TLS certificate validation | `0` (insecure, dev only) |
| `NODE_ICU_DATA` | Path to custom ICU data file | `/usr/share/icu/icudt72l.dat` |
| `UV_THREADPOOL_SIZE` | libuv thread pool size (default: 4) | `8` (max: 1024) |
| `NODE_NO_WARNINGS` | Suppress all process warnings | `1` |
| `NODE_PENDING_DEPRECATION` | Emit pending deprecation warnings | `1` |
| `NODE_DISABLE_COLORS` | Disable ANSI colors in stdout | `1` |
| `NO_COLOR` | Disable colors (standard, all tools) | `1` |
| `FORCE_COLOR` | Force color output even in non-TTY | `1`, `2`, or `3` (color depth) |
| `NODE_EXTRA_CA_CERTS` | Path to additional CA certificates PEM file | `/etc/ssl/custom-ca.pem` |
| `NODE_REDIRECT_WARNINGS` | Write warnings to a file instead of stderr | `/var/log/node-warnings.log` |
| `TZ` | Timezone for `Date` and `Intl` | `America/New_York` |

> [!WARNING]
> Setting `NODE_TLS_REJECT_UNAUTHORIZED=0` disables TLS certificate verification globally. Never do this in production — it makes your application vulnerable to man-in-the-middle attacks. Use `NODE_EXTRA_CA_CERTS` to add custom CA certificates instead.

**Setting environment variables:**

```bash
# Inline for one command
NODE_ENV=production node server.js

# Export for the session
export NODE_ENV=production
export UV_THREADPOOL_SIZE=8
node server.js

# In package.json scripts
{
  "scripts": {
    "start": "NODE_ENV=production node server.js",
    "dev": "NODE_ENV=development node --watch server.js"
  }
}
```

### 3.2 The .env File Support

Node.js has built-in `.env` file loading via the `--env-file` flag:

```bash
node --env-file=.env server.js
```

**`.env` file format:**

```text
# Comments start with #
NODE_ENV=production
DATABASE_URL=postgres://localhost:5432/mydb
API_KEY=sk-abc123
PORT=3000

# Multi-line values use quotes
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA...
-----END RSA PRIVATE KEY-----"
```

You can load multiple `.env` files (later files override earlier):

```bash
node --env-file=.env --env-file=.env.local server.js
```

> [!NOTE]
> The `--env-file` flag loads variables into `process.env` before your code runs. It does **not** override existing environment variables — system-set variables take precedence.

### 3.3 Package.json Configuration

The `package.json` file controls project metadata, dependencies, and module resolution:

```json
{
  "name": "my-app",
  "version": "1.0.0",
  "type": "module",
  "main": "./src/index.js",
  "exports": {
    ".": "./src/index.js",
    "./utils": "./src/utils.js"
  },
  "engines": {
    "node": ">=20.0.0"
  },
  "scripts": {
    "start": "node src/index.js",
    "dev": "node --watch src/index.js",
    "test": "node --test"
  }
}
```

**Key fields:**

| Field | Purpose |
|-------|---------|
| `"type": "module"` | Treat `.js` files as ES Modules (import/export syntax) |
| `"type": "commonjs"` | Treat `.js` files as CommonJS (require syntax) — this is the default |
| `"main"` | Entry point for `require('package-name')` |
| `"exports"` | Modern entry point mapping (takes precedence over `"main"`) |
| `"imports"` | Private import maps (subpath imports starting with `#`) |
| `"engines"` | Declare required Node.js/npm versions |

### 3.4 TypeScript Configuration

Node.js supports TypeScript via `--experimental-strip-types`, which removes type annotations at load time (powered by Amaro/SWC):

```bash
node --experimental-strip-types app.ts
```

For projects using TypeScript, create a `tsconfig.json`:

```json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "esModuleInterop": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "declaration": true,
    "sourceMap": true
  },
  "include": ["src/**/*"]
}
```

> [!IMPORTANT]
> The `--experimental-strip-types` flag only removes type annotations — it does **not** support TypeScript features that require code transformation, such as `enum`, `namespace`, or legacy decorators. Use `tsc` or a bundler for those features.

### 3.5 NODE_OPTIONS

`NODE_OPTIONS` lets you set CLI flags via environment variable, useful for production deployments where you cannot modify the `node` command:

```bash
# Increase heap memory
export NODE_OPTIONS="--max-old-space-size=4096"

# Enable source maps
export NODE_OPTIONS="--enable-source-maps"

# Combine multiple flags
export NODE_OPTIONS="--max-old-space-size=4096 --enable-source-maps"
```

Not all flags are allowed in `NODE_OPTIONS` for security reasons. Flags like `--eval`, `--print`, and `--input-type` are disallowed.

For complete configuration reference, runtime tuning, V8 flags, and memory management, see the **Admin Guide**.

---

## 4. Core Usage

### 4.1 Running JavaScript

#### Basic Execution

```bash
# Run a file
node app.js

# Evaluate code inline
node --eval "console.log(2 + 2)"
# Output: 4

# Evaluate and print the result (no console.log needed)
node --print "Math.PI * 2"
# Output: 6.283185307179586

# Read from stdin
echo 'console.log("from stdin")' | node
# Output: from stdin
```

#### The REPL

Start an interactive Read-Eval-Print Loop:

```bash
node
```

```text
Welcome to Node.js v22.14.0.
Type ".help" for more information.
> 2 + 2
4
> const fs = require('node:fs')
undefined
> fs.readdirSync('.').length
12
> .exit
```

**REPL special commands:**

| Command | Action |
|---------|--------|
| `.help` | Show available commands |
| `.break` | Abort multi-line input |
| `.clear` | Reset context |
| `.editor` | Enter multi-line editor mode |
| `.exit` | Exit the REPL |
| `.load filename` | Load a file into the session |
| `.save filename` | Save the session to a file |

**Tab completion** works for globals, modules, and object properties. Press Tab twice to see all completions.

#### Watch Mode

Automatically restart when files change:

```bash
# Watch the entry file and its dependencies
node --watch app.js

# Watch specific paths
node --watch-path=./src --watch-path=./config app.js

# Watch mode with inspect
node --watch --inspect app.js
```

```text
[watch] restarting due to changes...
[watch] restarted successfully
```

> [!TIP]
> Watch mode is built into Node.js since v18.11. It monitors the entry file and all transitively imported/required files. Use `--watch-path` to include configuration files or directories not directly imported.

### 4.2 Module Systems

Node.js supports two module systems: CommonJS (the original) and ES Modules (the standard).

#### CommonJS (CJS)

```javascript
// math.js — exporting
function add(a, b) {
  return a + b;
}

function multiply(a, b) {
  return a * b;
}

module.exports = { add, multiply };
```

```javascript
// app.js — importing
const { add, multiply } = require("./math.js");

console.log(add(2, 3));       // 5
console.log(multiply(4, 5));  // 20
```

**CommonJS characteristics:**

- `require()` is synchronous
- `module.exports` defines what a module exposes
- Files are wrapped in a function: `(function(exports, require, module, __filename, __dirname) { ... })`
- Circular dependencies are handled (partial exports returned)

#### ES Modules (ESM)

```javascript
// math.mjs — exporting
export function add(a, b) {
  return a + b;
}

export function multiply(a, b) {
  return a * b;
}

export default { add, multiply };
```

```javascript
// app.mjs — importing
import { add, multiply } from "./math.mjs";
import math from "./math.mjs";

console.log(add(2, 3));          // 5
console.log(math.multiply(4, 5)); // 20
```

**How Node.js decides which system to use:**

| Condition | Module System |
|-----------|--------------|
| `.mjs` extension | Always ES Modules |
| `.cjs` extension | Always CommonJS |
| `.js` extension + `"type": "module"` in nearest `package.json` | ES Modules |
| `.js` extension + `"type": "commonjs"` or no `"type"` in nearest `package.json` | CommonJS |

> [!IMPORTANT]
> ES Modules are asynchronous and have different scoping rules. `require()`, `__filename`, `__dirname`, `module`, and `exports` are NOT available in ES Modules. Use `import.meta.url` and `import.meta.dirname` (v21.2+) instead.

**ES Module equivalents for CommonJS globals:**

```javascript
// __filename equivalent
import { fileURLToPath } from "node:url";
const __filename = fileURLToPath(import.meta.url);

// __dirname equivalent
import { dirname } from "node:path";
const __dirname = dirname(fileURLToPath(import.meta.url));

// Or in Node.js 21.2+:
const __dirname = import.meta.dirname;
const __filename = import.meta.filename;

// require() equivalent for JSON
import { readFileSync } from "node:fs";
const pkg = JSON.parse(readFileSync("./package.json", "utf-8"));

// Or with import attributes (experimental)
import pkg from "./package.json" with { type: "json" };
```

#### Importing CommonJS from ESM and Vice Versa

```javascript
// ESM can import CommonJS
import cjsModule from "./legacy.cjs";

// CommonJS can import ESM (async)
async function main() {
  const esmModule = await import("./modern.mjs");
}
main();
```

### 4.3 File System Operations

The `node:fs` module provides file system access. It offers three API styles: callback, synchronous, and promise-based.

#### Reading Files

```javascript
import { readFile, readFileSync } from "node:fs";
import { readFile as readFileAsync } from "node:fs/promises";

// Callback style
readFile("data.txt", "utf-8", (err, data) => {
  if (err) throw err;
  console.log(data);
});

// Synchronous (blocks the event loop)
const content = readFileSync("data.txt", "utf-8");
console.log(content);

// Promise style (recommended)
const data = await readFileAsync("data.txt", "utf-8");
console.log(data);
```

#### Writing Files

```javascript
import { writeFile, appendFile } from "node:fs/promises";

// Write (creates or overwrites)
await writeFile("output.txt", "Hello, World!\n", "utf-8");

// Append
await appendFile("log.txt", `${new Date().toISOString()} Event occurred\n`);

// Write with options
await writeFile("config.json", JSON.stringify(config, null, 2), {
  encoding: "utf-8",
  mode: 0o644,
  flag: "wx", // fail if file exists
});
```

#### Directory Operations

```javascript
import { readdir, mkdir, rm, stat } from "node:fs/promises";

// List directory contents
const files = await readdir("./src");
console.log(files); // ['index.js', 'utils.js', 'lib']

// List with file types
const entries = await readdir("./src", { withFileTypes: true });
for (const entry of entries) {
  console.log(`${entry.name} - ${entry.isDirectory() ? "dir" : "file"}`);
}

// Recursive directory listing
const allFiles = await readdir("./src", { recursive: true });

// Create directory (recursive)
await mkdir("./data/logs/2026", { recursive: true });

// Remove directory (recursive)
await rm("./temp", { recursive: true, force: true });

// File/directory info
const info = await stat("./package.json");
console.log(`Size: ${info.size} bytes`);
console.log(`Modified: ${info.mtime}`);
console.log(`Is file: ${info.isFile()}`);
```

#### Watching for File Changes

```javascript
import { watch } from "node:fs/promises";

const watcher = watch("./src", { recursive: true });

for await (const event of watcher) {
  console.log(`${event.eventType}: ${event.filename}`);
}
```

> [!NOTE]
> The `fs.watch()` API is platform-dependent. On Linux, it uses inotify; on macOS, FSEvents; on Windows, ReadDirectoryChangesW. Behavior for recursive watching varies — it is fully supported on macOS and Windows, and on Linux since Node.js 19.

#### File Operations

```javascript
import { copyFile, rename, access, constants } from "node:fs/promises";

// Copy a file
await copyFile("source.txt", "dest.txt");

// Rename / move
await rename("old.txt", "new.txt");

// Check if file exists and is readable
try {
  await access("config.json", constants.R_OK);
  console.log("File is readable");
} catch {
  console.log("File is not readable or does not exist");
}
```

### 4.4 HTTP/HTTPS Servers

#### Basic HTTP Server

```javascript
import http from "node:http";

const server = http.createServer((req, res) => {
  const { method, url, headers } = req;

  if (method === "GET" && url === "/") {
    res.writeHead(200, { "Content-Type": "text/html" });
    res.end("<h1>Welcome</h1>");
    return;
  }

  if (method === "GET" && url === "/api/health") {
    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(JSON.stringify({ status: "ok", uptime: process.uptime() }));
    return;
  }

  if (method === "POST" && url === "/api/data") {
    let body = "";
    req.on("data", (chunk) => (body += chunk));
    req.on("end", () => {
      const data = JSON.parse(body);
      res.writeHead(201, { "Content-Type": "application/json" });
      res.end(JSON.stringify({ received: data }));
    });
    return;
  }

  res.writeHead(404, { "Content-Type": "text/plain" });
  res.end("Not Found");
});

server.listen(3000, () => {
  console.log("Listening on http://localhost:3000");
});
```

#### HTTPS Server

```javascript
import https from "node:https";
import { readFileSync } from "node:fs";

const options = {
  key: readFileSync("./certs/server.key"),
  cert: readFileSync("./certs/server.cert"),
};

const server = https.createServer(options, (req, res) => {
  res.writeHead(200);
  res.end("Secure connection established\n");
});

server.listen(443, () => {
  console.log("HTTPS server running on port 443");
});
```

#### Making HTTP Requests

**Using the global `fetch` (recommended):**

```javascript
// GET request
const response = await fetch("https://api.example.com/data");
const data = await response.json();
console.log(data);

// POST request
const result = await fetch("https://api.example.com/submit", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ name: "Node.js", version: 22 }),
});
console.log(result.status); // 201
```

**Using `http.get` (lower level):**

```javascript
import http from "node:http";

http.get("http://example.com", (res) => {
  let data = "";
  res.on("data", (chunk) => (data += chunk));
  res.on("end", () => console.log(data));
});
```

### 4.5 Streams

Streams are fundamental to Node.js — they let you process data piece by piece without loading it all into memory.

#### Stream Types

| Type | Description | Example |
|------|-------------|---------|
| `Readable` | Source of data | `fs.createReadStream()`, `http.IncomingMessage` |
| `Writable` | Destination for data | `fs.createWriteStream()`, `http.ServerResponse` |
| `Duplex` | Both readable and writable | `net.Socket`, `zlib` streams |
| `Transform` | Modify data as it passes through | `zlib.createGzip()`, `crypto.createCipheriv()` |

#### Reading a Large File with Streams

```javascript
import { createReadStream } from "node:fs";

const stream = createReadStream("large-file.log", { encoding: "utf-8" });

let lineCount = 0;
let remainder = "";

stream.on("data", (chunk) => {
  const lines = (remainder + chunk).split("\n");
  remainder = lines.pop(); // last partial line
  lineCount += lines.length;
});

stream.on("end", () => {
  if (remainder) lineCount++;
  console.log(`Total lines: ${lineCount}`);
});
```

#### Pipeline (Recommended for Connecting Streams)

```javascript
import { pipeline } from "node:stream/promises";
import { createReadStream, createWriteStream } from "node:fs";
import { createGzip } from "node:zlib";

// Compress a file
await pipeline(
  createReadStream("input.txt"),
  createGzip(),
  createWriteStream("input.txt.gz")
);

console.log("Compression complete");
```

> [!CAUTION]
> Never pipe streams manually with `.pipe()` in production without error handling. Use `stream.pipeline()` instead — it properly handles errors and cleans up resources (destroys streams) when any stream in the chain fails or the destination closes prematurely.

#### Custom Transform Stream

```javascript
import { Transform } from "node:stream";

const upperCase = new Transform({
  transform(chunk, encoding, callback) {
    callback(null, chunk.toString().toUpperCase());
  },
});

process.stdin.pipe(upperCase).pipe(process.stdout);
```

### 4.6 Child Processes

The `node:child_process` module lets you run shell commands, other programs, and Node.js scripts as separate processes.

```javascript
import { exec, execFile, spawn, fork } from "node:child_process";
import { execSync } from "node:child_process";
```

#### exec — Run a Shell Command

```javascript
import { exec } from "node:child_process";

exec("ls -la", (error, stdout, stderr) => {
  if (error) {
    console.error(`Error: ${error.message}`);
    return;
  }
  console.log(stdout);
});

// Promise version
import { exec as execCb } from "node:child_process";
import { promisify } from "node:util";
const execAsync = promisify(execCb);

const { stdout } = await execAsync("git log --oneline -5");
console.log(stdout);
```

#### spawn — Stream Output from a Long-Running Process

```javascript
import { spawn } from "node:child_process";

const child = spawn("find", [".", "-name", "*.js"]);

child.stdout.on("data", (data) => {
  console.log(`stdout: ${data}`);
});

child.stderr.on("data", (data) => {
  console.error(`stderr: ${data}`);
});

child.on("close", (code) => {
  console.log(`Process exited with code ${code}`);
});
```

#### fork — Run a Node.js Script with IPC

```javascript
// parent.js
import { fork } from "node:child_process";

const child = fork("./worker.js");

child.on("message", (msg) => {
  console.log("From child:", msg);
});

child.send({ task: "compute", data: [1, 2, 3, 4, 5] });
```

```javascript
// worker.js
process.on("message", (msg) => {
  if (msg.task === "compute") {
    const result = msg.data.reduce((a, b) => a + b, 0);
    process.send({ result });
  }
});
```

#### execFile — Run a Binary Directly (No Shell)

```javascript
import { execFile } from "node:child_process";

execFile("python3", ["--version"], (error, stdout) => {
  console.log(stdout.trim()); // Python 3.12.0
});
```

#### Synchronous Execution

```javascript
import { execSync, spawnSync } from "node:child_process";

// Quick shell command
const output = execSync("whoami", { encoding: "utf-8" });
console.log(output.trim());

// With error handling
try {
  execSync("false");
} catch (error) {
  console.log(`Command failed with code ${error.status}`);
}
```

### 4.7 Worker Threads

Worker threads enable true parallelism for CPU-intensive tasks without the overhead of separate processes.

```javascript
// main.js
import { Worker, isMainThread, parentPort, workerData } from "node:worker_threads";

if (isMainThread) {
  // Main thread — create workers
  const worker = new Worker(new URL(import.meta.url), {
    workerData: { start: 0, end: 1_000_000 },
  });

  worker.on("message", (result) => {
    console.log(`Sum: ${result}`);
  });

  worker.on("error", (err) => {
    console.error("Worker error:", err);
  });

  worker.on("exit", (code) => {
    if (code !== 0) console.error(`Worker exited with code ${code}`);
  });
} else {
  // Worker thread — do computation
  const { start, end } = workerData;
  let sum = 0;
  for (let i = start; i < end; i++) {
    sum += i;
  }
  parentPort.postMessage(sum);
}
```

**Worker pool pattern:**

```javascript
import { Worker } from "node:worker_threads";
import { cpus } from "node:os";

class WorkerPool {
  #workers = [];
  #queue = [];

  constructor(workerFile, poolSize = cpus().length) {
    for (let i = 0; i < poolSize; i++) {
      this.#addWorker(workerFile);
    }
  }

  #addWorker(workerFile) {
    const worker = new Worker(workerFile);
    worker.busy = false;
    worker.on("message", (result) => {
      worker.busy = false;
      worker.currentResolve(result);
      this.#processQueue();
    });
    this.#workers.push(worker);
  }

  #processQueue() {
    if (this.#queue.length === 0) return;
    const freeWorker = this.#workers.find((w) => !w.busy);
    if (!freeWorker) return;
    const { data, resolve } = this.#queue.shift();
    freeWorker.busy = true;
    freeWorker.currentResolve = resolve;
    freeWorker.postMessage(data);
  }

  run(data) {
    return new Promise((resolve) => {
      const freeWorker = this.#workers.find((w) => !w.busy);
      if (freeWorker) {
        freeWorker.busy = true;
        freeWorker.currentResolve = resolve;
        freeWorker.postMessage(data);
      } else {
        this.#queue.push({ data, resolve });
      }
    });
  }
}
```

> [!TIP]
> Use worker threads for CPU-bound tasks (image processing, compression, heavy math). For I/O-bound tasks (HTTP requests, file reads, database queries), async/await with the event loop is more efficient and simpler.

### 4.8 Events and EventEmitter

```javascript
import { EventEmitter } from "node:events";

class OrderSystem extends EventEmitter {
  placeOrder(order) {
    // Process order...
    this.emit("order:placed", order);
  }

  cancelOrder(orderId) {
    // Cancel order...
    this.emit("order:cancelled", { orderId });
  }
}

const orders = new OrderSystem();

orders.on("order:placed", (order) => {
  console.log(`New order: ${order.id}`);
});

orders.on("order:placed", (order) => {
  // Send notification email...
});

orders.once("order:cancelled", (info) => {
  console.log(`First cancellation: ${info.orderId}`);
});

// Async event handling
import { once } from "node:events";

const [order] = await once(orders, "order:placed");
console.log("Got order:", order);
```

**Key methods:**

| Method | Description |
|--------|-------------|
| `emitter.on(event, listener)` | Add a persistent listener |
| `emitter.once(event, listener)` | Add a one-time listener |
| `emitter.off(event, listener)` | Remove a listener |
| `emitter.emit(event, ...args)` | Emit an event |
| `emitter.removeAllListeners(event?)` | Remove all listeners |
| `emitter.listenerCount(event)` | Count listeners for an event |
| `emitter.setMaxListeners(n)` | Set max listener warning threshold (default: 10) |
| `events.once(emitter, event)` | Promise-based one-time listener |

### 4.9 Buffers and Binary Data

Buffers represent fixed-length raw binary data. Essential for file I/O, network protocols, and binary formats.

```javascript
import { Buffer } from "node:buffer";

// Create buffers
const buf1 = Buffer.alloc(10);          // 10 zero-filled bytes
const buf2 = Buffer.from("Hello");      // From string (UTF-8)
const buf3 = Buffer.from([0x48, 0x69]); // From byte array
const buf4 = Buffer.from("SGVsbG8=", "base64"); // From base64

// Read buffer content
console.log(buf2.toString());          // "Hello"
console.log(buf2.toString("hex"));     // "48656c6c6f"
console.log(buf2.toString("base64"));  // "SGVsbG8="
console.log(buf3.toString());          // "Hi"

// Buffer operations
console.log(buf2.length);              // 5
console.log(buf2[0]);                  // 72 (ASCII 'H')
console.log(buf2.includes("ell"));     // true
console.log(Buffer.compare(buf1, buf2)); // -1, 0, or 1

// Concatenate
const combined = Buffer.concat([buf2, Buffer.from(" "), buf3]);
console.log(combined.toString()); // "Hello Hi"

// Slice (shares memory!)
const slice = buf2.subarray(0, 3);
console.log(slice.toString()); // "Hel"
```

### 4.10 URL and Query String Parsing

```javascript
import { URL, URLSearchParams } from "node:url";

// Parse a URL
const url = new URL("https://example.com:8080/path/to/page?name=Node&v=22#section");

console.log(url.hostname);    // "example.com"
console.log(url.port);        // "8080"
console.log(url.pathname);    // "/path/to/page"
console.log(url.search);      // "?name=Node&v=22"
console.log(url.hash);        // "#section"
console.log(url.origin);      // "https://example.com:8080"

// Work with query parameters
const params = url.searchParams;
console.log(params.get("name"));   // "Node"
console.log(params.has("v"));      // true
params.append("lang", "en");
console.log(url.toString());
// https://example.com:8080/path/to/page?name=Node&v=22&lang=en#section

// Build a URL
const apiUrl = new URL("/api/v1/users", "https://example.com");
apiUrl.searchParams.set("page", "1");
apiUrl.searchParams.set("limit", "50");
console.log(apiUrl.toString());
// https://example.com/api/v1/users?page=1&limit=50
```

### 4.11 Path Manipulation

```javascript
import path from "node:path";

// Join path segments safely
const fullPath = path.join("/home", "user", "documents", "file.txt");
// Linux: /home/user/documents/file.txt
// Windows: \home\user\documents\file.txt

// Resolve to absolute path
const abs = path.resolve("src", "utils", "index.js");
// e.g., /Users/dev/project/src/utils/index.js

// Parse path components
const parsed = path.parse("/home/user/file.txt");
console.log(parsed);
// { root: '/', dir: '/home/user', base: 'file.txt', ext: '.txt', name: 'file' }

// Common operations
console.log(path.basename("/path/to/file.js"));      // "file.js"
console.log(path.basename("/path/to/file.js", ".js")); // "file"
console.log(path.dirname("/path/to/file.js"));        // "/path/to"
console.log(path.extname("archive.tar.gz"));          // ".gz"
console.log(path.relative("/a/b/c", "/a/d/e"));       // "../../d/e"
console.log(path.isAbsolute("/home/user"));            // true
console.log(path.isAbsolute("./relative"));            // false

// Platform-specific separator
console.log(path.sep); // "/" on POSIX, "\\" on Windows

// Normalize messy paths
console.log(path.normalize("/foo/bar//baz/./quux/..")); // "/foo/bar/baz"
```

> [!WARNING]
> Always use `path.join()` or `path.resolve()` instead of string concatenation for paths. Concatenation with `"/"` breaks on Windows; `path.join()` uses the correct separator automatically.

### 4.12 Crypto Operations

The `node:crypto` module provides cryptographic functionality including hashing, encryption, signing, and random number generation.

#### Hashing

```javascript
import { createHash, randomBytes, randomUUID } from "node:crypto";

// SHA-256 hash
const hash = createHash("sha256")
  .update("Hello, World!")
  .digest("hex");
console.log(hash);
// dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f

// File hash
import { createReadStream } from "node:fs";

async function hashFile(filepath) {
  const hash = createHash("sha256");
  const stream = createReadStream(filepath);
  for await (const chunk of stream) {
    hash.update(chunk);
  }
  return hash.digest("hex");
}

// Random values
const token = randomBytes(32).toString("hex");    // 64-char hex token
const uuid = randomUUID();                        // v4 UUID
```

#### Encryption and Decryption

```javascript
import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "node:crypto";

const algorithm = "aes-256-gcm";
const password = "super-secret-password";
const salt = randomBytes(16);
const key = scryptSync(password, salt, 32);

function encrypt(plaintext) {
  const iv = randomBytes(16);
  const cipher = createCipheriv(algorithm, key, iv);

  let encrypted = cipher.update(plaintext, "utf-8", "hex");
  encrypted += cipher.final("hex");
  const authTag = cipher.getAuthTag().toString("hex");

  return { encrypted, iv: iv.toString("hex"), authTag };
}

function decrypt({ encrypted, iv, authTag }) {
  const decipher = createDecipheriv(algorithm, key, Buffer.from(iv, "hex"));
  decipher.setAuthTag(Buffer.from(authTag, "hex"));

  let decrypted = decipher.update(encrypted, "hex", "utf-8");
  decrypted += decipher.final("utf-8");
  return decrypted;
}

const encrypted = encrypt("Hello, World!");
console.log("Encrypted:", encrypted);

const decrypted = decrypt(encrypted);
console.log("Decrypted:", decrypted); // "Hello, World!"
```

#### HMAC Signing

```javascript
import { createHmac } from "node:crypto";

const secret = "webhook-secret-key";
const payload = JSON.stringify({ event: "push", repo: "myrepo" });

const signature = createHmac("sha256", secret)
  .update(payload)
  .digest("hex");

console.log(`sha256=${signature}`);
// Verify: recompute and compare with timing-safe equality
```

### 4.13 DNS Resolution

```javascript
import { promises as dns } from "node:dns";

// Resolve hostname to IP
const addresses = await dns.resolve4("nodejs.org");
console.log(addresses); // ['104.20.22.46', '104.20.23.46']

// Reverse lookup
const hostnames = await dns.reverse("8.8.8.8");
console.log(hostnames); // ['dns.google']

// MX records
const mx = await dns.resolveMx("gmail.com");
console.log(mx);
// [{ exchange: 'alt1.gmail-smtp-in.l.google.com', priority: 10 }, ...]

// All record types
const allRecords = await dns.resolveAny("example.com");

// Lookup (uses OS resolver, respects /etc/hosts)
const result = await dns.lookup("localhost");
console.log(result); // { address: '127.0.0.1', family: 4 }
```

> [!NOTE]
> `dns.lookup()` uses the operating system's DNS resolution (getaddrinfo), which respects `/etc/hosts` and system DNS configuration. `dns.resolve*()` methods connect directly to DNS servers, bypassing the OS resolver. In most application code, `dns.lookup()` is what you want.

### 4.14 Timers

```javascript
// setTimeout — run once after delay
const timeoutId = setTimeout(() => {
  console.log("Runs after 2 seconds");
}, 2000);

// clearTimeout — cancel
clearTimeout(timeoutId);

// setInterval — run repeatedly
const intervalId = setInterval(() => {
  console.log("Every 5 seconds:", new Date().toISOString());
}, 5000);

// Stop after 30 seconds
setTimeout(() => clearInterval(intervalId), 30000);

// setImmediate — run at end of current event loop iteration
setImmediate(() => {
  console.log("Runs after I/O callbacks");
});

// Promise-based timers (Node.js 16+)
import { setTimeout as sleep } from "node:timers/promises";

await sleep(1000);
console.log("1 second later");

// AbortController integration
const controller = new AbortController();
try {
  await sleep(60000, null, { signal: controller.signal });
} catch (err) {
  if (err.name === "AbortError") {
    console.log("Timer was cancelled");
  }
}
// Cancel from elsewhere: controller.abort();

// Scheduling timer with ref/unref
const timer = setTimeout(() => {}, 60000);
timer.unref(); // Don't keep the process alive for this timer
```

### 4.15 Built-in Test Runner

Node.js includes a test runner in the `node:test` module. No external testing framework required.

#### Writing Tests

```javascript
// test/math.test.js
import { describe, it, before, after, beforeEach, mock } from "node:test";
import assert from "node:assert/strict";

import { add, multiply, divide } from "../src/math.js";

describe("Math module", () => {
  describe("add()", () => {
    it("should add two positive numbers", () => {
      assert.strictEqual(add(2, 3), 5);
    });

    it("should handle negative numbers", () => {
      assert.strictEqual(add(-1, -2), -3);
    });

    it("should handle zero", () => {
      assert.strictEqual(add(0, 5), 5);
    });
  });

  describe("divide()", () => {
    it("should divide numbers", () => {
      assert.strictEqual(divide(10, 2), 5);
    });

    it("should throw on division by zero", () => {
      assert.throws(() => divide(1, 0), {
        message: "Division by zero",
      });
    });
  });
});
```

#### Running Tests

```bash
# Run all tests in test/ directory
node --test

# Run specific test files
node --test test/math.test.js test/utils.test.js

# Run with glob pattern
node --test "test/**/*.test.js"

# Watch mode — rerun on changes
node --test --watch

# With coverage
node --test --experimental-test-coverage

# Specific reporter
node --test --test-reporter=spec
node --test --test-reporter=dot
node --test --test-reporter=tap

# Only run tests matching a name pattern
node --test --test-name-pattern="add"

# Concurrency control
node --test --test-concurrency=4
```

Expected output (spec reporter):

```text
▶ Math module
  ▶ add()
    ✔ should add two positive numbers (0.5ms)
    ✔ should handle negative numbers (0.1ms)
    ✔ should handle zero (0.1ms)
  ▶ divide()
    ✔ should divide numbers (0.1ms)
    ✔ should throw on division by zero (0.2ms)

ℹ tests 5
ℹ suites 3
ℹ pass 5
ℹ fail 0
ℹ duration_ms 45
```

#### Mocking

```javascript
import { describe, it, mock } from "node:test";
import assert from "node:assert/strict";

describe("Mocking", () => {
  it("should mock a function", () => {
    const fn = mock.fn((x) => x * 2);

    assert.strictEqual(fn(5), 10);
    assert.strictEqual(fn.mock.calls.length, 1);
    assert.deepStrictEqual(fn.mock.calls[0].arguments, [5]);
  });

  it("should mock timers", (t) => {
    t.mock.timers.enable({ apis: ["setTimeout"] });

    let called = false;
    setTimeout(() => { called = true; }, 5000);

    t.mock.timers.tick(5000);
    assert.strictEqual(called, true);
  });

  it("should mock a method", (t) => {
    const obj = {
      getValue() { return "real"; }
    };

    t.mock.method(obj, "getValue", () => "mocked");
    assert.strictEqual(obj.getValue(), "mocked");
  });
});
```

#### Subtests, Hooks, and Lifecycle

```javascript
import { describe, it, before, after, beforeEach, afterEach } from "node:test";

describe("Database tests", () => {
  let db;

  before(async () => {
    db = await connectToTestDb();
  });

  after(async () => {
    await db.close();
  });

  beforeEach(async () => {
    await db.clear();
  });

  it("should insert a record", async () => {
    await db.insert({ name: "test" });
    const count = await db.count();
    assert.strictEqual(count, 1);
  });
});
```

### 4.16 TypeScript Support

Node.js supports TypeScript directly via the `--experimental-strip-types` flag, which strips type annotations at load time:

```bash
node --experimental-strip-types app.ts
```

```typescript
// app.ts
interface User {
  name: string;
  age: number;
}

function greet(user: User): string {
  return `Hello, ${user.name}! You are ${user.age} years old.`;
}

const user: User = { name: "Alice", age: 30 };
console.log(greet(user));
```

```text
Hello, Alice! You are 30 years old.
```

**What works:**

- Type annotations (`: string`, `: number`, etc.)
- Interfaces
- Type aliases
- Generics
- `as` type assertions
- Optional chaining and nullish coalescing (these are JavaScript too)

**What does NOT work (requires a build step):**

- `enum` declarations
- `namespace` declarations
- Legacy decorators (Stage 2 decorators work)
- `const enum`
- Implicit file extensions (must use `.ts`)
- `import type` transformation

> [!IMPORTANT]
> Type stripping does not perform type checking. Run `tsc --noEmit` separately for type validation. The `--experimental-strip-types` flag is for runtime convenience, not as a replacement for the TypeScript compiler.

### 4.17 Debugging

#### Inspector Protocol (Chrome DevTools)

```bash
# Start with inspector on default port 9229
node --inspect app.js

# Break on first line
node --inspect-brk app.js

# Custom port
node --inspect=0.0.0.0:9230 app.js
```

```text
Debugger listening on ws://127.0.0.1:9229/6f8c1e3a-...
For help, see: https://nodejs.org/en/docs/inspector
```

Open `chrome://inspect` in Chrome, click "Open dedicated DevTools for Node", and connect.

#### Built-in Debugger (CLI)

```bash
node inspect app.js
```

```text
< Debugger listening on ws://127.0.0.1:9229/...
connecting to 127.0.0.1:9229 ... ok
Break on start in app.js:1
> 1 const http = require('node:http');
  2
  3 const server = http.createServer((req, res) => {
debug>
```

**Debugger commands:**

| Command | Action |
|---------|--------|
| `cont`, `c` | Continue |
| `next`, `n` | Step over |
| `step`, `s` | Step into |
| `out`, `o` | Step out |
| `pause` | Pause running code |
| `repl` | Enter REPL to evaluate expressions |
| `watch('expr')` | Add watch expression |
| `setBreakpoint(line)`, `sb(line)` | Set breakpoint |
| `clearBreakpoint(file, line)`, `cb(file, line)` | Clear breakpoint |
| `backtrace`, `bt` | Print call stack |
| `.exit` | Exit debugger |

#### Programmatic Debugging

```javascript
// Add debugger statement in code
function processData(data) {
  debugger; // Execution pauses here when inspector is attached
  return data.map((item) => item * 2);
}
```

#### Debug Logging with NODE_DEBUG

```bash
# Enable debug output for specific modules
NODE_DEBUG=http,net node app.js
```

```text
HTTP 12345: call onSocket 0 0
HTTP 12345: createConnection example.com:80
NET 12345: connect: find host example.com
NET 12345: connect: dns options ...
```

### 4.18 Permission Model

The permission model restricts what a Node.js application can access, providing defense in depth.

```bash
# Enable the permission model (denies everything by default)
node --experimental-permission app.js

# Allow reading specific directories
node --experimental-permission --allow-fs-read=/app/data --allow-fs-read=/app/config app.js

# Allow writing to specific directory
node --experimental-permission --allow-fs-write=/app/output app.js

# Allow child process spawning
node --experimental-permission --allow-child-process app.js

# Allow worker threads
node --experimental-permission --allow-worker app.js

# Combine permissions
node --experimental-permission \
  --allow-fs-read=/app \
  --allow-fs-write=/app/output \
  --allow-child-process \
  app.js

# Allow all reads (wildcard)
node --experimental-permission --allow-fs-read=* app.js
```

**Permission types:**

| Permission | Flag | Controls |
|-----------|------|----------|
| File system read | `--allow-fs-read` | `fs.readFile`, `fs.readdir`, `fs.stat`, etc. |
| File system write | `--allow-fs-write` | `fs.writeFile`, `fs.mkdir`, `fs.unlink`, etc. |
| Child process | `--allow-child-process` | `child_process.spawn`, `exec`, etc. |
| Worker threads | `--allow-worker` | `new Worker()` |
| Addons | `--allow-addons` | Loading native C++ addons |
| WASI | `--allow-wasi` | WebAssembly System Interface |
| FFI | `--allow-ffi` | Foreign Function Interface (experimental) |

**Runtime permission checking:**

```javascript
// Check if a permission is granted
if (process.permission.has("fs.read", "/etc/passwd")) {
  console.log("Can read /etc/passwd");
} else {
  console.log("No read access to /etc/passwd");
}
```

> [!CAUTION]
> The permission model is experimental. While it provides useful sandboxing, do not rely on it as your sole security boundary. Combine it with OS-level sandboxing (containers, seccomp, AppArmor) for defense in depth. Native addons can bypass the permission model entirely unless `--allow-addons` restrictions are used.

---

## 5. Common Tasks

### 5.1 Create an HTTP REST API Server

```javascript
import http from "node:http";

const todos = [];
let nextId = 1;

function parseBody(req) {
  return new Promise((resolve, reject) => {
    let body = "";
    req.on("data", (chunk) => (body += chunk));
    req.on("end", () => {
      try {
        resolve(body ? JSON.parse(body) : {});
      } catch (e) {
        reject(new Error("Invalid JSON"));
      }
    });
    req.on("error", reject);
  });
}

function sendJson(res, statusCode, data) {
  res.writeHead(statusCode, {
    "Content-Type": "application/json",
    "Access-Control-Allow-Origin": "*",
  });
  res.end(JSON.stringify(data));
}

const server = http.createServer(async (req, res) => {
  const url = new URL(req.url, `http://${req.headers.host}`);
  const path = url.pathname;
  const method = req.method;

  try {
    // GET /api/todos
    if (method === "GET" && path === "/api/todos") {
      return sendJson(res, 200, todos);
    }

    // GET /api/todos/:id
    const idMatch = path.match(/^\/api\/todos\/(\d+)$/);
    if (method === "GET" && idMatch) {
      const todo = todos.find((t) => t.id === parseInt(idMatch[1]));
      if (!todo) return sendJson(res, 404, { error: "Not found" });
      return sendJson(res, 200, todo);
    }

    // POST /api/todos
    if (method === "POST" && path === "/api/todos") {
      const body = await parseBody(req);
      const todo = { id: nextId++, title: body.title, done: false };
      todos.push(todo);
      return sendJson(res, 201, todo);
    }

    // PATCH /api/todos/:id
    if (method === "PATCH" && idMatch) {
      const todo = todos.find((t) => t.id === parseInt(idMatch[1]));
      if (!todo) return sendJson(res, 404, { error: "Not found" });
      const body = await parseBody(req);
      Object.assign(todo, body);
      return sendJson(res, 200, todo);
    }

    // DELETE /api/todos/:id
    if (method === "DELETE" && idMatch) {
      const index = todos.findIndex((t) => t.id === parseInt(idMatch[1]));
      if (index === -1) return sendJson(res, 404, { error: "Not found" });
      todos.splice(index, 1);
      return sendJson(res, 204, null);
    }

    sendJson(res, 404, { error: "Not found" });
  } catch (err) {
    sendJson(res, 400, { error: err.message });
  }
});

server.listen(3000, () => console.log("API on http://localhost:3000"));
```

### 5.2 Read and Write JSON Files

```javascript
import { readFile, writeFile } from "node:fs/promises";

// Read JSON
async function loadConfig(path) {
  const raw = await readFile(path, "utf-8");
  return JSON.parse(raw);
}

// Write JSON (pretty-printed)
async function saveConfig(path, data) {
  await writeFile(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
}

// Atomic write (write to temp, then rename)
import { rename } from "node:fs/promises";
import { randomBytes } from "node:crypto";

async function atomicWriteJson(filePath, data) {
  const tmp = `${filePath}.${randomBytes(6).toString("hex")}.tmp`;
  await writeFile(tmp, JSON.stringify(data, null, 2) + "\n", "utf-8");
  await rename(tmp, filePath);
}

// Usage
const config = await loadConfig("./config.json");
config.updatedAt = new Date().toISOString();
await atomicWriteJson("./config.json", config);
```

### 5.3 Parse Command-Line Arguments with util.parseArgs

```javascript
import { parseArgs } from "node:util";

const { values, positionals } = parseArgs({
  options: {
    name: { type: "string", short: "n" },
    verbose: { type: "boolean", short: "v", default: false },
    port: { type: "string", short: "p", default: "3000" },
    help: { type: "boolean", short: "h", default: false },
  },
  allowPositionals: true,
  strict: true,
});

if (values.help) {
  console.log(`
Usage: node cli.js [options] <files...>

Options:
  -n, --name <name>    Your name
  -v, --verbose        Enable verbose output
  -p, --port <port>    Port number (default: 3000)
  -h, --help           Show help
  `);
  process.exit(0);
}

console.log("Name:", values.name);
console.log("Verbose:", values.verbose);
console.log("Port:", values.port);
console.log("Files:", positionals);
```

```bash
node cli.js --name "Node" -v -p 8080 file1.txt file2.txt
```

```text
Name: Node
Verbose: true
Port: 8080
Files: [ 'file1.txt', 'file2.txt' ]
```

### 5.4 Make HTTP Requests with fetch

```javascript
// GET request
const response = await fetch("https://jsonplaceholder.typicode.com/posts/1");
const post = await response.json();
console.log(post.title);

// POST with JSON body
const newPost = await fetch("https://jsonplaceholder.typicode.com/posts", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    title: "New Post",
    body: "Content here",
    userId: 1,
  }),
});
console.log("Created:", newPost.status); // 201

// With timeout using AbortController
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);

try {
  const res = await fetch("https://api.example.com/data", {
    signal: controller.signal,
  });
  const data = await res.json();
  console.log(data);
} catch (err) {
  if (err.name === "AbortError") {
    console.error("Request timed out");
  } else {
    throw err;
  }
} finally {
  clearTimeout(timeout);
}

// Stream a large response
import { createWriteStream } from "node:fs";

const res = await fetch("https://example.com/large-file");
const fileStream = createWriteStream("./downloaded.bin");
for await (const chunk of res.body) {
  fileStream.write(chunk);
}
fileStream.end();
```

### 5.5 Work with Environment Variables

```javascript
// Read environment variables
const port = process.env.PORT || 3000;
const nodeEnv = process.env.NODE_ENV || "development";
const dbUrl = process.env.DATABASE_URL;

if (!dbUrl) {
  console.error("DATABASE_URL is required");
  process.exit(1);
}

// Check environment
const isDev = nodeEnv === "development";
const isProd = nodeEnv === "production";

// Set at runtime (affects current process only)
process.env.MY_VAR = "value";

// Use with --env-file
// node --env-file=.env app.js

// Pattern: configuration object
const config = {
  port: parseInt(process.env.PORT || "3000", 10),
  host: process.env.HOST || "0.0.0.0",
  db: {
    url: process.env.DATABASE_URL,
    pool: parseInt(process.env.DB_POOL_SIZE || "10", 10),
  },
  logLevel: process.env.LOG_LEVEL || "info",
};
```

### 5.6 Use async/await with fs.promises

```javascript
import { readFile, writeFile, readdir, stat, mkdir } from "node:fs/promises";
import path from "node:path";

// Process all JSON files in a directory
async function processJsonFiles(dir) {
  const entries = await readdir(dir, { withFileTypes: true });

  for (const entry of entries) {
    const fullPath = path.join(dir, entry.name);

    if (entry.isDirectory()) {
      await processJsonFiles(fullPath); // Recurse
      continue;
    }

    if (path.extname(entry.name) !== ".json") continue;

    const content = JSON.parse(await readFile(fullPath, "utf-8"));
    console.log(`${entry.name}: ${Object.keys(content).length} keys`);
  }
}

// Parallel file operations
const files = ["a.txt", "b.txt", "c.txt"];
const contents = await Promise.all(
  files.map((f) => readFile(f, "utf-8"))
);

// Copy directory recursively
async function copyDir(src, dest) {
  await mkdir(dest, { recursive: true });
  const entries = await readdir(src, { withFileTypes: true });

  for (const entry of entries) {
    const srcPath = path.join(src, entry.name);
    const destPath = path.join(dest, entry.name);

    if (entry.isDirectory()) {
      await copyDir(srcPath, destPath);
    } else {
      await writeFile(destPath, await readFile(srcPath));
    }
  }
}
```

### 5.7 Stream Large Files

```javascript
import { createReadStream, createWriteStream } from "node:fs";
import { pipeline } from "node:stream/promises";
import { Transform } from "node:stream";

// Copy a large file efficiently
await pipeline(
  createReadStream("input.dat"),
  createWriteStream("output.dat")
);

// Process a large CSV line by line
import { createInterface } from "node:readline";

const rl = createInterface({
  input: createReadStream("data.csv"),
  crlfDelay: Infinity,
});

let lineNum = 0;
for await (const line of rl) {
  lineNum++;
  if (lineNum === 1) continue; // Skip header
  const [name, email, age] = line.split(",");
  // Process each row...
}
console.log(`Processed ${lineNum - 1} records`);

// Transform stream: convert CSV to JSON lines
const csvToJson = new Transform({
  objectMode: true,
  transform(chunk, encoding, callback) {
    const line = chunk.toString().trim();
    const [name, email, age] = line.split(",");
    callback(null, JSON.stringify({ name, email, age: parseInt(age) }) + "\n");
  },
});

await pipeline(
  createReadStream("data.csv"),
  csvToJson,
  createWriteStream("data.jsonl")
);
```

### 5.8 Create a WebSocket Client

Node.js includes a built-in `WebSocket` client (global, no import needed) since v21, following the browser WebSocket API:

```javascript
const ws = new WebSocket("ws://localhost:8080");

ws.addEventListener("open", () => {
  console.log("Connected");
  ws.send("Hello, server!");
});

ws.addEventListener("message", (event) => {
  console.log("Received:", event.data);
});

ws.addEventListener("close", () => {
  console.log("Disconnected");
});

ws.addEventListener("error", (err) => {
  console.error("WebSocket error:", err);
});
```

> [!NOTE]
> Node.js includes a built-in `WebSocket` **client** since v21. For a WebSocket **server**, you need a third-party library such as `ws`. The built-in client follows the browser WebSocket API standard.

### 5.9 Run Tests with the Built-in Test Runner

```javascript
// test/api.test.js
import { describe, it, before, after } from "node:test";
import assert from "node:assert/strict";
import http from "node:http";

describe("API Integration Tests", () => {
  let server;
  let baseUrl;

  before((_, done) => {
    server = http.createServer((req, res) => {
      if (req.url === "/health") {
        res.writeHead(200, { "Content-Type": "application/json" });
        res.end(JSON.stringify({ status: "ok" }));
      } else {
        res.writeHead(404);
        res.end();
      }
    });
    server.listen(0, () => {
      baseUrl = `http://localhost:${server.address().port}`;
      done();
    });
  });

  after((_, done) => {
    server.close(done);
  });

  it("GET /health returns 200", async () => {
    const res = await fetch(`${baseUrl}/health`);
    assert.strictEqual(res.status, 200);

    const body = await res.json();
    assert.strictEqual(body.status, "ok");
  });

  it("GET /unknown returns 404", async () => {
    const res = await fetch(`${baseUrl}/unknown`);
    assert.strictEqual(res.status, 404);
  });
});
```

Run:

```bash
node --test test/api.test.js
```

### 5.10 Use Worker Threads for CPU-Intensive Tasks

```javascript
// hash-worker.js
import { parentPort } from "node:worker_threads";
import { createHash } from "node:crypto";

parentPort.on("message", ({ algorithm, data }) => {
  const hash = createHash(algorithm).update(data).digest("hex");
  parentPort.postMessage(hash);
});
```

```javascript
// main.js
import { Worker } from "node:worker_threads";

function hashInWorker(algorithm, data) {
  return new Promise((resolve, reject) => {
    const worker = new Worker("./hash-worker.js");
    worker.postMessage({ algorithm, data });
    worker.on("message", resolve);
    worker.on("error", reject);
  });
}

// Hash large data without blocking the event loop
const hash = await hashInWorker("sha256", "a".repeat(10_000_000));
console.log("Hash:", hash);
```

### 5.11 Create a CLI Tool

```javascript
#!/usr/bin/env node
// bin/mytool.js

import { parseArgs } from "node:util";
import { readFile, writeFile } from "node:fs/promises";

const { values, positionals } = parseArgs({
  options: {
    output: { type: "string", short: "o" },
    format: { type: "string", short: "f", default: "json" },
    pretty: { type: "boolean", default: false },
    help: { type: "boolean", short: "h", default: false },
    version: { type: "boolean", short: "V", default: false },
  },
  allowPositionals: true,
});

if (values.version) {
  const pkg = JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf-8"));
  console.log(pkg.version);
  process.exit(0);
}

if (values.help || positionals.length === 0) {
  console.log(`
Usage: mytool [options] <input-files...>

Options:
  -o, --output <file>     Output file (default: stdout)
  -f, --format <format>   Output format: json, csv (default: json)
  --pretty                Pretty-print output
  -h, --help              Show this help
  -V, --version           Show version
  `);
  process.exit(0);
}

// Process input files
const results = [];
for (const inputFile of positionals) {
  const content = await readFile(inputFile, "utf-8");
  results.push({ file: inputFile, lines: content.split("\n").length });
}

// Format output
let output;
if (values.format === "csv") {
  output = "file,lines\n" + results.map((r) => `${r.file},${r.lines}`).join("\n");
} else {
  output = values.pretty
    ? JSON.stringify(results, null, 2)
    : JSON.stringify(results);
}

// Write output
if (values.output) {
  await writeFile(values.output, output + "\n");
  console.error(`Written to ${values.output}`);
} else {
  console.log(output);
}
```

Add to `package.json`:

```json
{
  "bin": {
    "mytool": "./bin/mytool.js"
  }
}
```

Make executable and link:

```bash
chmod +x bin/mytool.js
npm link
mytool --help
```

### 5.12 Handle Signals and Graceful Shutdown

```javascript
import http from "node:http";

const server = http.createServer((req, res) => {
  res.end("OK");
});

const connections = new Set();

server.on("connection", (conn) => {
  connections.add(conn);
  conn.on("close", () => connections.delete(conn));
});

function shutdown(signal) {
  console.log(`\n${signal} received. Shutting down gracefully...`);

  // Stop accepting new connections
  server.close(() => {
    console.log("Server closed. Exiting.");
    process.exit(0);
  });

  // Close existing connections after a grace period
  setTimeout(() => {
    console.warn("Forcing shutdown after timeout");
    for (const conn of connections) {
      conn.destroy();
    }
    process.exit(1);
  }, 10000);
}

process.on("SIGTERM", () => shutdown("SIGTERM"));
process.on("SIGINT", () => shutdown("SIGINT"));

// Handle uncaught exceptions
process.on("uncaughtException", (err) => {
  console.error("Uncaught exception:", err);
  shutdown("uncaughtException");
});

// Handle unhandled promise rejections
process.on("unhandledRejection", (reason, promise) => {
  console.error("Unhandled rejection at:", promise, "reason:", reason);
  shutdown("unhandledRejection");
});

server.listen(3000, () => {
  console.log("Server listening on port 3000");
});
```

### 5.13 Use diagnostics_channel for Observability

```javascript
import diagnostics_channel from "node:diagnostics_channel";

// Create a channel
const httpChannel = diagnostics_channel.channel("app:http");
const dbChannel = diagnostics_channel.channel("app:db");

// Publisher (in your HTTP handler)
function handleRequest(req, res) {
  const start = performance.now();

  res.on("finish", () => {
    httpChannel.publish({
      method: req.method,
      url: req.url,
      status: res.statusCode,
      duration: performance.now() - start,
    });
  });

  res.end("OK");
}

// Subscriber (in your monitoring setup)
diagnostics_channel.subscribe("app:http", (message) => {
  console.log(`[HTTP] ${message.method} ${message.url} -> ${message.status} (${message.duration.toFixed(1)}ms)`);
});

// Subscribe to built-in Node.js channels
diagnostics_channel.subscribe("http.server.request.start", (message) => {
  console.log("Incoming request:", message.request.url);
});

diagnostics_channel.subscribe("http.client.request.start", (message) => {
  console.log("Outgoing request:", message.request.path);
});

// Tracing integration pattern
class Tracer {
  constructor() {
    diagnostics_channel.tracingChannel("http.server.request").subscribe({
      start(message) {
        message.span = { id: crypto.randomUUID(), startTime: Date.now() };
      },
      end(message) {
        const duration = Date.now() - message.span.startTime;
        console.log(`Request ${message.span.id} completed in ${duration}ms`);
      },
      error(message) {
        console.error(`Request ${message.span.id} failed:`, message.error);
      },
    });
  }
}
```

### 5.14 Profile with --prof and --cpu-prof

#### V8 CPU Profile (Chrome DevTools Format)

```bash
# Generate a CPU profile
node --cpu-prof --cpu-prof-interval=100 app.js
# Press Ctrl+C to stop; creates CPU.*.cpuprofile

# Specify output directory
node --cpu-prof --cpu-prof-dir=./profiles app.js

# Open in Chrome DevTools:
# 1. Open chrome://inspect
# 2. Click "Open dedicated DevTools for Node"
# 3. Go to "Performance" tab -> Load profile
```

#### V8 Tick Profiler

```bash
# Generate V8 log
node --prof app.js
# Creates isolate-0x*.log

# Process the log
node --prof-process isolate-0x*.log > profile.txt
```

The output shows statistical profiling data:

```text
 [Summary]:
   ticks  total  nonlib   name
    354   35.4%   49.3%  JavaScript
    298   29.8%   41.5%  C++
     66    6.6%    9.2%  GC
    247   24.7%          Shared libraries
    101   10.1%          Unaccounted

 [JavaScript]:
   ticks  total  nonlib   name
    127   12.7%   17.7%  LazyCompile: *processData app.js:15:21
     85    8.5%   11.8%  LazyCompile: *transform app.js:42:18
```

#### Heap Snapshot

```bash
# Generate heap snapshot on signal
node --heapsnapshot-signal=SIGUSR2 app.js

# In another terminal:
kill -USR2 <pid>
# Creates Heap.*.heapsnapshot - open in Chrome DevTools Memory tab
```

### 5.15 Use SQLite (node:sqlite)

Node.js includes a built-in SQLite module:

```javascript
import { DatabaseSync } from "node:sqlite";

// Open a database (creates file if it doesn't exist)
const db = new DatabaseSync("myapp.db");

// Create tables
db.exec(`
  CREATE TABLE IF NOT EXISTS users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    email TEXT UNIQUE NOT NULL,
    created_at TEXT DEFAULT (datetime('now'))
  )
`);

// Insert with prepared statement
const insert = db.prepare("INSERT INTO users (name, email) VALUES (?, ?)");
insert.run("Alice", "[email removed]");
insert.run("Bob", "[email removed]");

// Query
const select = db.prepare("SELECT * FROM users WHERE name = ?");
const user = select.get("Alice");
console.log(user);
// { id: 1, name: 'Alice', email: '[email removed]', created_at: '2026-05-13 ...' }

// Query all
const all = db.prepare("SELECT * FROM users");
const users = all.all();
console.log(users.length); // 2

// Close when done
db.close();
```

> [!NOTE]
> The `node:sqlite` module provides synchronous APIs via `DatabaseSync`. It is suitable for embedded databases, CLI tools, and local data storage. For high-concurrency server applications, consider enabling WAL mode: `db.exec("PRAGMA journal_mode=WAL")`.

### 5.16 Watch Mode for Development

```bash
# Watch and restart on changes to imported files
node --watch server.js

# Watch specific directories
node --watch-path=./src --watch-path=./config server.js

# Combine with other flags
node --watch --env-file=.env server.js

# Watch mode with test runner
node --test --watch
```

Watch mode output:

```text
Server running at http://localhost:3000
[watch] restarting due to changes in ./src/routes.js...
Server running at http://localhost:3000
```

### 5.17 Load .env Files

```bash
# Load a single .env file
node --env-file=.env app.js

# Load multiple (later overrides earlier)
node --env-file=.env --env-file=.env.local app.js

# Combine with other flags
node --env-file=.env --watch server.js
```

Create `.env`:

```text
PORT=3000
DATABASE_URL=postgres://localhost:5432/myapp
SECRET_KEY=dev-secret
NODE_ENV=development
```

Access in code:

```javascript
console.log(process.env.PORT);         // "3000"
console.log(process.env.DATABASE_URL); // "postgres://localhost:5432/myapp"
```

### 5.18 Build Single Executable Applications

Package a Node.js app into a single binary that includes the Node.js runtime:

**Step 1: Create a configuration file `sea-config.json`:**

```json
{
  "main": "app.js",
  "output": "sea-prep.blob",
  "disableExperimentalSEAWarning": true
}
```

**Step 2: Generate the preparation blob:**

```bash
node --experimental-sea-config sea-config.json
```

**Step 3: Copy the Node.js binary:**

```bash
# Linux/macOS
cp $(which node) myapp

# Windows
copy node.exe myapp.exe
```

**Step 4: Inject the blob into the binary:**

```bash
# Linux
npx postject myapp NODE_SEA_BLOB sea-prep.blob \
  --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2

# macOS
npx postject myapp NODE_SEA_BLOB sea-prep.blob \
  --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 \
  --macho-segment-name NODE_SEA

# Windows
npx postject myapp.exe NODE_SEA_BLOB sea-prep.blob ^
  --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2
```

**Step 5: Run the executable:**

```bash
./myapp
```

The resulting binary is self-contained — no Node.js installation needed on the target machine.

### 5.19 Cluster for Multi-Core Scaling

```javascript
import cluster from "node:cluster";
import http from "node:http";
import { availableParallelism } from "node:os";

const numCPUs = availableParallelism();

if (cluster.isPrimary) {
  console.log(`Primary ${process.pid} starting ${numCPUs} workers`);

  // Fork workers
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  // Restart workers that crash
  cluster.on("exit", (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died (${signal || code}). Restarting...`);
    cluster.fork();
  });

  // Graceful shutdown
  process.on("SIGTERM", () => {
    console.log("SIGTERM received, shutting down workers...");
    for (const id in cluster.workers) {
      cluster.workers[id].process.kill("SIGTERM");
    }
  });
} else {
  // Workers share the TCP connection
  const server = http.createServer((req, res) => {
    res.writeHead(200);
    res.end(`Worker ${process.pid} handled this request\n`);
  });

  server.listen(3000, () => {
    console.log(`Worker ${process.pid} started`);
  });
}
```

```text
Primary 12345 starting 8 workers
Worker 12346 started
Worker 12347 started
Worker 12348 started
...
```

### 5.20 Use the REPL Effectively

```bash
# Start with custom prompt
NODE_REPL_HISTORY=~/.node_history node

# Preload modules
node -e "globalThis.fs = require('node:fs'); globalThis.path = require('node:path')" -i

# REPL from within your app
```

```javascript
import repl from "node:repl";

const server = repl.start({
  prompt: "myapp> ",
  useColors: true,
});

// Add custom commands
server.defineCommand("status", {
  help: "Show application status",
  action() {
    console.log("Memory:", process.memoryUsage().heapUsed / 1024 / 1024, "MB");
    console.log("Uptime:", process.uptime(), "seconds");
    this.displayPrompt();
  },
});
```

REPL tips:

- `_` holds the result of the last expression
- `_error` holds the last error
- Tab completion works for all properties
- `.editor` for multi-line input
- `.load` and `.save` for session management

### 5.21 TLS/SSL Server Setup

```javascript
import tls from "node:tls";
import { readFileSync } from "node:fs";

const options = {
  key: readFileSync("server-key.pem"),
  cert: readFileSync("server-cert.pem"),
  ca: [readFileSync("ca-cert.pem")], // CA certificates for client verification
  requestCert: false,                 // Set true for mutual TLS
  minVersion: "TLSv1.2",
};

const server = tls.createServer(options, (socket) => {
  console.log("Client connected:", socket.authorized ? "authorized" : "unauthorized");
  socket.write("Welcome!\n");
  socket.pipe(socket); // Echo server
});

server.listen(8443, () => {
  console.log("TLS server listening on port 8443");
});

// Generate self-signed cert for testing:
// openssl req -x509 -newkey rsa:4096 -keyout server-key.pem -out server-cert.pem -days 365 -nodes
```

### 5.22 UDP / Datagram Sockets

```javascript
import dgram from "node:dgram";

// UDP Server
const server = dgram.createSocket("udp4");

server.on("message", (msg, rinfo) => {
  console.log(`Received: ${msg} from ${rinfo.address}:${rinfo.port}`);
  server.send(`Echo: ${msg}`, rinfo.port, rinfo.address);
});

server.on("listening", () => {
  const addr = server.address();
  console.log(`UDP server listening on ${addr.address}:${addr.port}`);
});

server.bind(41234);

// UDP Client
const client = dgram.createSocket("udp4");
const message = Buffer.from("Hello UDP");
client.send(message, 41234, "localhost", (err) => {
  if (err) console.error(err);
});

client.on("message", (msg) => {
  console.log(`Response: ${msg}`);
  client.close();
});
```

### 5.23 Compress and Decompress with zlib

```javascript
import { createGzip, createGunzip, gzip, gunzip } from "node:zlib";
import { pipeline } from "node:stream/promises";
import { createReadStream, createWriteStream } from "node:fs";
import { promisify } from "node:util";

// Stream-based compression (for large files)
await pipeline(
  createReadStream("input.log"),
  createGzip(),
  createWriteStream("input.log.gz")
);

// Stream-based decompression
await pipeline(
  createReadStream("input.log.gz"),
  createGunzip(),
  createWriteStream("input.log.restored")
);

// Buffer-based (for small data)
const gzipAsync = promisify(gzip);
const gunzipAsync = promisify(gunzip);

const compressed = await gzipAsync(Buffer.from("Hello, World!"));
console.log("Compressed size:", compressed.length);

const decompressed = await gunzipAsync(compressed);
console.log("Decompressed:", decompressed.toString()); // "Hello, World!"

// HTTP response compression
import http from "node:http";

http.createServer((req, res) => {
  const acceptEncoding = req.headers["accept-encoding"] || "";

  const body = "Response data here...".repeat(100);

  if (acceptEncoding.includes("gzip")) {
    res.writeHead(200, {
      "Content-Type": "text/plain",
      "Content-Encoding": "gzip",
    });
    const gzStream = createGzip();
    gzStream.pipe(res);
    gzStream.end(body);
  } else {
    res.writeHead(200, { "Content-Type": "text/plain" });
    res.end(body);
  }
}).listen(3000);
```

### 5.24 Source Map Support

```bash
# Enable source maps for better stack traces with transpiled code
node --enable-source-maps app.js
```

When using TypeScript or a bundler, source maps let you see original file names and line numbers in stack traces:

```javascript
// Without --enable-source-maps:
// Error at dist/app.js:42:15

// With --enable-source-maps:
// Error at src/app.ts:28:10
```

Set via environment variable:

```bash
export NODE_OPTIONS="--enable-source-maps"
```

### 5.25 Use the Permission Model for Sandboxing

```bash
# Run an untrusted script with minimal permissions
node --experimental-permission \
  --allow-fs-read=./data \
  untrusted-script.js

# A data processing tool that only reads input/ and writes to output/
node --experimental-permission \
  --allow-fs-read=./input \
  --allow-fs-write=./output \
  process-data.js

# A web server that can read its own directory but cannot spawn processes
node --experimental-permission \
  --allow-fs-read=/app \
  --allow-fs-write=/app/logs \
  server.js
```

**Check permissions programmatically:**

```javascript
// Detect if permission model is active
if (process.permission) {
  console.log("Permission model is active");

  // Check specific permissions
  console.log("Can read /etc:", process.permission.has("fs.read", "/etc"));
  console.log("Can write /tmp:", process.permission.has("fs.write", "/tmp"));
  console.log("Can spawn:", process.permission.has("child"));
  console.log("Can use workers:", process.permission.has("worker"));
}
```

### 5.26 Process and OS Information

```javascript
import os from "node:os";

// System info
console.log("Hostname:", os.hostname());
console.log("Platform:", os.platform());     // 'linux', 'darwin', 'win32'
console.log("Architecture:", os.arch());      // 'x64', 'arm64'
console.log("CPUs:", os.availableParallelism());
console.log("Total memory:", (os.totalmem() / 1024 ** 3).toFixed(1), "GB");
console.log("Free memory:", (os.freemem() / 1024 ** 3).toFixed(1), "GB");
console.log("Uptime:", (os.uptime() / 3600).toFixed(1), "hours");
console.log("Home dir:", os.homedir());
console.log("Temp dir:", os.tmpdir());

// CPU details
for (const cpu of os.cpus()) {
  console.log(`  ${cpu.model} @ ${cpu.speed} MHz`);
}

// Network interfaces
const nets = os.networkInterfaces();
for (const [name, addrs] of Object.entries(nets)) {
  for (const addr of addrs) {
    if (addr.family === "IPv4" && !addr.internal) {
      console.log(`${name}: ${addr.address}`);
    }
  }
}

// Process info
console.log("PID:", process.pid);
console.log("Node version:", process.version);
console.log("V8 version:", process.versions.v8);
console.log("Uptime:", process.uptime(), "seconds");
console.log("Memory:", process.memoryUsage());
// { rss: 30M, heapTotal: 6M, heapUsed: 4M, external: 1M, arrayBuffers: 0 }
```

### 5.27 Readline for Interactive Input

```javascript
import { stdin, stdout } from "node:process";

// Promise-based (Node.js 17+)
import { createInterface } from "node:readline/promises";

const rl = createInterface({ input: stdin, output: stdout });
const name = await rl.question("What is your name? ");
console.log(`Hello, ${name}!`);
rl.close();

// Interactive menu
async function menu() {
  const rl = createInterface({ input: stdin, output: stdout });

  while (true) {
    console.log("\n1. List files");
    console.log("2. Show date");
    console.log("3. Exit");
    const choice = await rl.question("\nChoice: ");

    switch (choice) {
      case "1":
        const { readdir } = await import("node:fs/promises");
        console.log((await readdir(".")).join(", "));
        break;
      case "2":
        console.log(new Date().toISOString());
        break;
      case "3":
        rl.close();
        return;
      default:
        console.log("Invalid choice");
    }
  }
}
```

### 5.28 Assert for Validation

```javascript
import assert from "node:assert/strict";

// Strict equality
assert.strictEqual(1 + 1, 2);
assert.strictEqual("hello", "hello");

// Deep equality (objects and arrays)
assert.deepStrictEqual({ a: 1, b: [2, 3] }, { a: 1, b: [2, 3] });

// Truthy/falsy
assert.ok(true);
assert.ok(1);

// Throws
assert.throws(() => {
  throw new Error("expected");
}, { message: "expected" });

// Rejects (async)
await assert.rejects(
  async () => { throw new Error("async error"); },
  { message: "async error" }
);

// Does not throw
assert.doesNotThrow(() => {
  return 42;
});

// Match (regex)
assert.match("hello world", /world/);
assert.doesNotMatch("hello world", /xyz/);
```

### 5.29 Performance Measurement

```javascript
import { performance, PerformanceObserver } from "node:perf_hooks";

// Basic timing
const start = performance.now();
// ... work ...
const duration = performance.now() - start;
console.log(`Took ${duration.toFixed(2)}ms`);

// Performance marks and measures
performance.mark("start-db-query");
// ... database query ...
performance.mark("end-db-query");
performance.measure("db-query", "start-db-query", "end-db-query");

// Observe measurements
const obs = new PerformanceObserver((items) => {
  for (const entry of items.getEntries()) {
    console.log(`${entry.name}: ${entry.duration.toFixed(2)}ms`);
  }
});
obs.observe({ entryTypes: ["measure"] });

// Function timing wrapper
function timeFunction(fn, label) {
  return function (...args) {
    const start = performance.now();
    const result = fn.apply(this, args);
    console.log(`${label}: ${(performance.now() - start).toFixed(2)}ms`);
    return result;
  };
}

// timerify — automatic function timing
const timedFunction = performance.timerify(myExpensiveFunction);
obs.observe({ entryTypes: ["function"] });
timedFunction(); // Automatically measured
```

### 5.30 Async Hooks for Context Tracking

```javascript
import { AsyncLocalStorage } from "node:async_hooks";

const asyncLocalStorage = new AsyncLocalStorage();

// Request ID tracking middleware pattern
function handleRequest(req, res) {
  const requestId = crypto.randomUUID();

  asyncLocalStorage.run({ requestId }, () => {
    // Everything in this callback and its async descendants
    // can access the request ID
    processRequest(req, res);
  });
}

function processRequest(req, res) {
  const store = asyncLocalStorage.getStore();
  log(`Processing request ${store.requestId}`);
  // Even through async boundaries, the store is available
  setTimeout(() => {
    const store = asyncLocalStorage.getStore();
    log(`Still have request ID: ${store.requestId}`);
    res.end("OK");
  }, 100);
}

function log(message) {
  const store = asyncLocalStorage.getStore();
  const prefix = store?.requestId ? `[${store.requestId}]` : "[no-context]";
  console.log(`${prefix} ${message}`);
}
```

---

## 6. Troubleshooting

### 6.1 Error: `Cannot find module '...'`

**Cause:** Node.js cannot locate the module you are trying to `require()` or `import`. The module may not be installed, the path may be wrong, or the module resolution mode (CJS vs ESM) may be mismatched.

**Solution:**

1. Check that the package is installed: `npm ls <package-name>`
2. Run `npm install` to install missing dependencies
3. Verify the import path is correct (relative paths need `./`)
4. Check that `"type": "module"` in `package.json` matches your syntax

**Prevention:** Use a lockfile (`package-lock.json`) and run `npm ci` in CI environments.

### 6.2 Error: `ERR_REQUIRE_ESM`

**Cause:** You used `require()` to load an ES Module package. Many modern packages are ESM-only.

**Solution:**

1. Switch to `import`: `import pkg from 'package-name'`
2. Set `"type": "module"` in your `package.json`
3. Or use dynamic import: `const pkg = await import('package-name')`

**Prevention:** When starting new projects, use `"type": "module"` from the start.

### 6.3 Error: `ERR_MODULE_NOT_FOUND` with `import`

**Cause:** ES module imports require full file extensions. Unlike `require()`, `import` does not auto-resolve `.js` or `index.js`.

**Solution:**

```javascript
// Wrong
import { utils } from "./utils";

// Correct
import { utils } from "./utils.js";
```

**Prevention:** Always include file extensions in ES module imports.

### 6.4 Error: `SyntaxError: Cannot use import statement outside a module`

**Cause:** You used `import` syntax in a file that Node.js treats as CommonJS.

**Solution:**

1. Add `"type": "module"` to your `package.json`
2. Or rename the file to `.mjs`
3. Or switch to `require()` syntax

### 6.5 Error: `RangeError: Maximum call stack size exceeded`

**Cause:** Infinite recursion or extremely deep recursion.

**Solution:**

1. Check for recursive function calls without proper base cases
2. Use iterative algorithms for deep data structures
3. Increase stack size if legitimately needed: `node --stack-size=8192 app.js`

**Prevention:** Always validate recursion base cases. Use tail-call patterns or convert to iterative loops.

### 6.6 Error: `FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory`

**Cause:** V8 ran out of heap memory. Default is approximately 1.5 GB for 64-bit systems.

**Solution:**

```bash
node --max-old-space-size=4096 app.js  # 4 GB heap
```

Or via environment variable:

```bash
export NODE_OPTIONS="--max-old-space-size=4096"
```

**Prevention:** Profile memory usage, use streams instead of loading entire files into memory, avoid memory leaks by properly removing event listeners and clearing intervals.

### 6.7 Error: `ECONNREFUSED` or `ECONNRESET`

**Cause:** Connection to a remote service was refused or reset. The target server may be down, the port may be wrong, or a firewall is blocking the connection.

**Solution:**

1. Verify the target service is running: `curl http://localhost:3000`
2. Check the host and port in your connection string
3. Check firewall rules
4. Verify DNS resolution: `node -e "require('dns').lookup('hostname', console.log)"`

**Prevention:** Implement retry logic with exponential backoff for external service calls.

### 6.8 Error: `EADDRINUSE: address already in use :::3000`

**Cause:** Another process is already listening on the specified port.

**Solution:**

```bash
# Find what's using the port
# Linux/macOS
lsof -i :3000
# Windows
netstat -ano | findstr :3000

# Kill the process
kill <PID>

# Or use a different port
PORT=3001 node server.js
```

**Prevention:** Make the port configurable via environment variable. Handle the error gracefully:

```javascript
server.on("error", (err) => {
  if (err.code === "EADDRINUSE") {
    console.error(`Port ${port} is in use. Try: PORT=${port + 1} node server.js`);
    process.exit(1);
  }
});
```

### 6.9 Error: `EMFILE: too many open files`

**Cause:** The process has exceeded the OS limit for open file descriptors.

**Solution:**

```bash
# Check current limit
ulimit -n

# Increase limit (Linux/macOS)
ulimit -n 65536

# Permanent change (Linux): add to /etc/security/limits.conf
# * soft nofile 65536
# * hard nofile 65536
```

**Prevention:** Use streaming APIs instead of opening many files simultaneously. Limit concurrency with a semaphore pattern.

### 6.10 Error: `ERR_OSSL_EVP_UNSUPPORTED`

**Cause:** OpenSSL 3.0 (bundled in Node.js 17+) removed support for some legacy cryptographic algorithms used by older packages.

**Solution:**

```bash
node --openssl-legacy-provider app.js
```

**Prevention:** Update packages that rely on legacy algorithms. Use `npm audit` to identify outdated dependencies.

### 6.11 Error: `ENOSPC: System limit for number of file watchers reached`

**Cause:** Linux inotify watcher limit reached. Common when using `--watch` mode or tools that watch many files.

**Solution:**

```bash
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
```

**Prevention:** Set the higher limit in your system configuration or Docker image.

### 6.12 Error: `ERR_HTTP_HEADERS_SENT`

**Cause:** Attempting to set headers or send a response after the response has already been sent.

**Solution:**

```javascript
// Wrong — sends headers twice
function handler(req, res) {
  if (condition) {
    res.json({ ok: true });
    // Missing return! Falls through to next response
  }
  res.json({ error: "not ok" });
}

// Correct
function handler(req, res) {
  if (condition) {
    return res.json({ ok: true });
  }
  res.json({ error: "not ok" });
}
```

**Prevention:** Always `return` after sending a response in conditional branches.

### 6.13 Error: `UnhandledPromiseRejection`

**Cause:** A Promise was rejected but no `.catch()` handler or `try/catch` block caught the error.

**Solution:**

```javascript
// Add catch handler
fetchData().catch((err) => console.error("Failed:", err));

// Or use try/catch with async/await
try {
  await fetchData();
} catch (err) {
  console.error("Failed:", err);
}
```

**Prevention:** Enable the `--unhandled-rejections=throw` flag (default since Node.js 15) to crash on unhandled rejections. Add a global handler for logging:

```javascript
process.on("unhandledRejection", (reason) => {
  console.error("Unhandled rejection:", reason);
  process.exit(1);
});
```

### 6.14 Error: `DEPTH_ZERO_SELF_SIGNED_CERT` or `UNABLE_TO_VERIFY_LEAF_SIGNATURE`

**Cause:** TLS connection to a server with a self-signed or improperly chained certificate.

**Solution:**

```javascript
// For a specific request (preferred)
import https from "node:https";
import { readFileSync } from "node:fs";

const agent = new https.Agent({
  ca: readFileSync("./custom-ca.pem"),
});

// Or add custom CA globally
// NODE_EXTRA_CA_CERTS=./custom-ca.pem node app.js
```

**Prevention:** Use `NODE_EXTRA_CA_CERTS` for internal CAs. Never set `NODE_TLS_REJECT_UNAUTHORIZED=0` in production.

### 6.15 Error: `ETIMEOUT` or `ETIMEDOUT`

**Cause:** A network operation timed out — DNS resolution, TCP connection, or HTTP response took too long.

**Solution:**

1. Check network connectivity
2. Increase timeout values
3. Add retry logic

```javascript
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 10000);

try {
  const response = await fetch(url, { signal: controller.signal });
} finally {
  clearTimeout(timeout);
}
```

### 6.16 Error: `ENOMEM: not enough memory`

**Cause:** The system ran out of physical memory or the container memory limit was hit.

**Solution:**

1. Increase container memory limits
2. Reduce `--max-old-space-size` to stay within limits
3. Profile memory usage: `node --inspect app.js` and use Chrome DevTools Memory tab

**Prevention:** Set explicit memory limits and monitor with `process.memoryUsage()`.

### 6.17 Warning: `MaxListenersExceededWarning`

**Cause:** More than 10 listeners added to a single event. Usually indicates a memory leak from adding listeners in a loop without removing them.

**Solution:**

```javascript
// If intentional, increase the limit
emitter.setMaxListeners(25);

// If a leak, remove listeners when done
const handler = () => { /* ... */ };
emitter.on("event", handler);
// Later:
emitter.off("event", handler);
```

**Prevention:** Use `once()` for one-time listeners. Track listener additions in loops.

### 6.18 Error: `EACCES: permission denied`

**Cause:** The process does not have file system permissions to access the path, or on Linux, binding to ports below 1024 requires root.

**Solution:**

```bash
# Fix file permissions
chmod 644 config.json
chown $(whoami) data/

# For port 80/443, use a reverse proxy (nginx/caddy) instead of running as root
```

### 6.19 Error: `ERR_WORKER_OUT_OF_MEMORY`

**Cause:** A worker thread exceeded its memory limit.

**Solution:**

```javascript
const worker = new Worker("./worker.js", {
  resourceLimits: {
    maxOldGenerationSizeMb: 512,
    maxYoungGenerationSizeMb: 64,
  },
});
```

### 6.20 Error: `CERT_HAS_EXPIRED`

**Cause:** The TLS certificate of the remote server has expired.

**Solution:**

1. Contact the server administrator to renew the certificate
2. For testing only: `NODE_TLS_REJECT_UNAUTHORIZED=0 node app.js`

**Prevention:** Monitor certificate expiration dates.

### 6.21 Error: `dns.resolveSrv` ECONNREFUSED on Windows

**Cause:** This is a known issue (GitHub [#62326](https://github.com/nodejs/node/issues/62326)). DNS SRV resolution can fail with ECONNREFUSED on Windows systems due to how the c-ares resolver interacts with Windows DNS.

**Solution:**

1. Use `dns.lookup()` instead if possible (uses OS resolver)
2. Set a custom DNS server: `dns.setServers(['8.8.8.8'])`
3. Retry with a fallback resolver

### 6.22 Stack Traces Broken with `--enable-source-maps`

**Cause:** Known issue (GitHub [#55236](https://github.com/nodejs/node/issues/55236)). Source map support can produce incorrect or missing stack trace lines in certain cases.

**Solution:**

1. Verify your source maps are valid (use a source map validator to check)
2. Try without `--enable-source-maps` to get V8's raw traces
3. Use `--inspect` and Chrome DevTools which have independent source map handling

### 6.23 Composed Streams Hang Since Node.js 22.5

**Cause:** Known regression (GitHub [#60083](https://github.com/nodejs/node/issues/60083)). Composed streams using `stream.compose()` may hang in certain pipeline configurations.

**Solution:**

1. Use `pipeline()` instead of `stream.compose()`
2. Pin to Node.js 22.4 if you depend on composed streams
3. Monitor the issue for a fix in a patch release

### 6.24 Error: `ERR_INVALID_PACKAGE_CONFIG`

**Cause:** The `package.json` file has invalid JSON or an invalid `"exports"` configuration.

**Solution:**

1. Validate JSON syntax: `node -e "JSON.parse(require('fs').readFileSync('package.json'))"`
2. Check `"exports"` map for missing entries
3. Ensure `"type"` is either `"module"` or `"commonjs"`

### 6.25 Error: `ERR_UNKNOWN_FILE_EXTENSION` for `.ts` Files

**Cause:** Running a TypeScript file without the `--experimental-strip-types` flag.

**Solution:**

```bash
node --experimental-strip-types app.ts
```

Or register a custom loader:

```bash
node --import tsx app.ts
```

**Prevention:** Add the flag to your `package.json` scripts or `NODE_OPTIONS`.

### 6.26 Slow Startup Time

**Cause:** Loading too many modules at startup, or modules with expensive initialization.

**Solution:**

1. Profile startup: `node --cpu-prof --cpu-prof-interval=10 app.js` (stop quickly with Ctrl+C)
2. Lazy-load modules that are not needed immediately
3. Use `--v8-pool-size=0` to reduce V8's background thread overhead for CLI tools
4. Consider a snapshot (V8 startup snapshot) for complex applications

### 6.27 Error: `EPERM: operation not permitted` with Permission Model

**Cause:** The `--experimental-permission` flag is active and the operation is not in the allow list.

**Solution:**

1. Add the required permission flag: `--allow-fs-read=/path/to/files`
2. Check which permission is needed: `process.permission.has("fs.read", path)`

---

## 7. FAQ

### General

**Q: What version of Node.js should I use?**

Use the current LTS (Long-Term Support) release for production applications. LTS versions receive security updates and bug fixes for 30 months. Even-numbered major versions (18, 20, 22) become LTS. Odd-numbered versions are short-lived "Current" releases for testing new features.

**Q: What is the difference between Node.js and Deno or Bun?**

All three are JavaScript runtimes built on V8 (Bun uses JavaScriptCore). Node.js is the most mature with the largest ecosystem. Deno has built-in TypeScript, a permission system, and web-standard APIs by default. Bun focuses on speed and includes a bundler. Node.js has adopted many of Deno's ideas — permission model, built-in test runner, TypeScript support, `fetch()`.

**Q: Is Node.js single-threaded?**

The JavaScript event loop runs on a single thread, but Node.js itself is multi-threaded. I/O operations (file system, network, DNS) are handled by a libuv thread pool (default 4 threads, configurable via `UV_THREADPOOL_SIZE`). You can use `worker_threads` for CPU-bound parallelism.

**Q: How do I check which Node.js version introduced a feature?**

Check the Node.js changelog or use `node -p "process.features"` for compile-time features. The [Node.js API docs](https://nodejs.org/docs/latest/api/) list "Added in" versions for every API.

**Q: What is the difference between npm, yarn, and pnpm?**

All are package managers for Node.js. `npm` ships with Node.js. `yarn` (by Facebook) introduced lockfiles and workspaces. `pnpm` uses a content-addressable store for disk-efficient installations. All use the same `npmjs.com` registry.

### Modules and Dependencies

**Q: Should I use CommonJS or ES Modules?**

For new projects, use ES Modules (`"type": "module"` in `package.json`). ESM is the JavaScript standard, supports `import`/`export`, has better tree-shaking, and is the direction Node.js is heading. CommonJS (`require`) is still fully supported and necessary for some older packages.

**Q: Can I use `require()` in ES Modules?**

Not directly. Use `import` instead, or `createRequire`:

```javascript
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const pkg = require("./package.json");
```

**Q: How do I import JSON in ES Modules?**

```javascript
// Option 1: Read and parse
import { readFileSync } from "node:fs";
const config = JSON.parse(readFileSync("./config.json", "utf-8"));

// Option 2: Import attribute (experimental)
import config from "./config.json" with { type: "json" };
```

**Q: What does the `node:` prefix mean in imports?**

The `node:` prefix explicitly imports Node.js built-in modules: `import fs from "node:fs"`. It prevents conflicts with npm packages that have the same name. Available since Node.js 16.0.0, recommended for all new code.

**Q: How do I handle circular dependencies?**

CommonJS returns a partial export object when circular requires are detected. ES Modules use live bindings, which generally work but can cause `undefined` values if accessed before initialization. Restructure your code to avoid circular dependencies when possible — extract shared code into a separate module.

### Performance

**Q: How do I increase the memory limit?**

```bash
node --max-old-space-size=4096 app.js  # 4 GB
```

The default is approximately 1.5 GB on 64-bit systems. For containers, ensure the Node.js memory limit is below the container memory limit.

**Q: How many concurrent connections can Node.js handle?**

Node.js can handle tens of thousands of concurrent connections on a single thread because connections are non-blocking. The practical limit depends on your OS (file descriptor limits), memory, and application complexity. Use `cluster` or multiple instances behind a load balancer for higher throughput.

**Q: Should I use cluster or worker_threads?**

Use `cluster` for scaling HTTP servers across CPU cores — each worker is a separate process with its own memory. Use `worker_threads` for CPU-intensive computation within a single application — threads share memory via `SharedArrayBuffer` and have lower overhead than processes.

**Q: How do I detect memory leaks?**

1. Monitor `process.memoryUsage().heapUsed` over time
2. Take heap snapshots: `node --inspect app.js` and use Chrome DevTools Memory tab
3. Use `--heap-prof` for heap allocation profiles
4. Watch for growing listener counts: `emitter.listenerCount('event')`

**Q: What is the libuv thread pool and when should I increase it?**

libuv uses a thread pool (default size: 4) for operations that cannot be done asynchronously at the OS level: DNS resolution (`dns.lookup`), file system operations, and some crypto operations. Increase it if your application does heavy file I/O or DNS lookups:

```bash
UV_THREADPOOL_SIZE=16 node app.js
```

### TypeScript

**Q: How do I run TypeScript without a build step?**

```bash
node --experimental-strip-types app.ts
```

This strips type annotations at load time. It does not support `enum`, `namespace`, or features requiring code transformation.

**Q: Can I use TypeScript with the built-in test runner?**

Yes:

```bash
node --experimental-strip-types --test "test/**/*.test.ts"
```

**Q: Do I still need `tsc`?**

Yes, for type checking. `--experimental-strip-types` only removes types — it does not validate them. Run `tsc --noEmit` for type checking, and use `node --experimental-strip-types` for execution.

### Security

**Q: How do I handle secrets and API keys?**

1. Use environment variables: `process.env.API_KEY`
2. Load from `.env` files: `node --env-file=.env app.js`
3. Never commit secrets to source control
4. In production, use a secrets manager (AWS Secrets Manager, HashiCorp Vault, etc.)

**Q: What is the permission model and should I use it?**

The permission model (`--experimental-permission`) restricts what your application can do: file system access, child process spawning, worker threads, and native addons. Use it when running untrusted code or as defense in depth. It is experimental — combine with OS-level sandboxing.

**Q: How do I keep dependencies secure?**

```bash
npm audit              # Check for known vulnerabilities
npm audit fix          # Auto-fix where possible
npm outdated           # Check for outdated packages
npm update             # Update within semver ranges
```

Run `npm audit` in CI and block deployments on critical vulnerabilities.

**Q: Is `eval()` safe to use?**

No. `eval()` executes arbitrary code and is a severe security risk if any part of the input comes from users. Use `JSON.parse()` for data, `vm.runInNewContext()` for sandboxed execution (with caveats), or the permission model for defense in depth.

### Debugging and Diagnostics

**Q: How do I debug a Node.js application?**

```bash
node --inspect app.js       # Attach Chrome DevTools
node --inspect-brk app.js   # Break on first line
node inspect app.js         # CLI debugger
```

Open `chrome://inspect` in Chrome to connect. VS Code also has built-in Node.js debugging support.

**Q: How do I profile CPU usage?**

```bash
node --cpu-prof app.js      # Chrome DevTools .cpuprofile format
node --prof app.js          # V8 tick profile (process with --prof-process)
```

**Q: How do I get a heap dump?**

```bash
node --heapsnapshot-signal=SIGUSR2 app.js
# Then: kill -USR2 <pid>
```

Or programmatically:

```javascript
import v8 from "node:v8";
v8.writeHeapSnapshot();
```

**Q: What does `NODE_DEBUG` do?**

It enables debug logging for Node.js core modules. Set it to a comma-separated list of module names:

```bash
NODE_DEBUG=http,net,tls node app.js
```

This prints internal debug information to stderr — useful for understanding what Node.js is doing under the hood.

### Development Workflow

**Q: How do I auto-restart my server during development?**

```bash
node --watch server.js
```

Built-in since Node.js 18.11. No external tools needed.

**Q: How do I use `.env` files?**

```bash
node --env-file=.env app.js
```

Built-in since Node.js 20.6.

**Q: How do I run a quick script without creating a file?**

```bash
node -e "console.log(process.versions)"
node -p "require('os').cpus().length"
```

**Q: Can Node.js execute shell commands?**

Yes, via `child_process`:

```javascript
import { execSync } from "node:child_process";
const result = execSync("ls -la", { encoding: "utf-8" });
```

**Q: How do I handle graceful shutdown?**

Listen for `SIGTERM` and `SIGINT`, stop accepting new connections, drain existing connections, then exit. See the Graceful Shutdown recipe in Section 5.12.

**Q: How do I test my code?**

Use the built-in test runner:

```bash
node --test
```

Or with watch mode:

```bash
node --test --watch
```

No external testing framework is required for most use cases.

**Q: What is the `node:` import prefix and should I use it?**

Yes. `import fs from "node:fs"` explicitly imports the Node.js built-in module, preventing accidental shadowing by npm packages. Always use it for built-in modules.

### Deployment

**Q: How should I set `NODE_ENV` in production?**

```bash
NODE_ENV=production node server.js
```

Many frameworks and libraries optimize behavior when `NODE_ENV=production`: disabling debug output, enabling caching, and reducing logging verbosity. Always set it in production.

**Q: What Docker base image should I use?**

| Image | Size | Use Case |
|-------|------|----------|
| `node:22-alpine` | ~50 MB | Production (smallest, musl libc) |
| `node:22-slim` | ~80 MB | Production (glibc, more compatible) |
| `node:22` | ~350 MB | Development (includes build tools) |

Use multi-stage builds: build in `node:22`, run in `node:22-alpine`.

**Q: How do I run Node.js as a system service?**

Use `systemd` on Linux:

```ini
[Unit]
Description=My Node.js App
After=network.target

[Service]
Type=simple
User=nodeapp
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/node server.js
Restart=on-failure
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target
```

Or use a process manager like PM2, or run in a container orchestrator (Docker Compose, Kubernetes).

---

_Generated May 2026 from nodejs/node at 978a8c4. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
