|$ curl https://forge-ai.dev/api/markdown?path=docs/nodejs
$cat docs/node.js.md
updated Recently·22 min read·published

Node.js

Node.jsBeginner to Advanced🎯Free Tools
Introduction

Node.js is a JavaScript runtime built on Chrome's V8 engine. It enables developers to run JavaScript outside the browser, on servers, CLIs, embedded devices, and cloud functions. Its non-blocking I/O model and event-driven architecture make it particularly effective for I/O-bound workloads such as APIs, real-time services, and streaming pipelines.

Since its release in 2009, Node.js has become one of the most widely used server-side platforms. The npm ecosystem hosts millions of packages, and modern Node.js 20+ ships with stable ESM support, built-in test runners, structured diagnostics, and a robust set of core APIs for networking, cryptography, file system access, and process management.

This section covers everything from the event loop and module systems to production deployment with Docker and PM2. Each guide includes real APIs, runnable code, edge cases, and common mistakes observed in production codebases.

What Makes Node.js Different

Traditional server platforms use a thread-per-request model. Each incoming request is assigned an operating system thread, which consumes memory and incurs context-switching overhead. Under high concurrency, this model struggles with memory exhaustion and thread scheduling latency.

Node.js uses a single event loop with a thread pool for asynchronous operations. I/O calls are offloaded to libuv, which notifies the event loop upon completion. As long as your application code does not perform long-running CPU work on the main thread, a single Node.js process can handle tens of thousands of concurrent connections with modest memory usage.

CharacteristicThread-per-RequestNode.js Event Loop
Memory per connection1-2 MB thread stackSmall heap object per connection
Blocking behaviorBlocks only current threadBlocks entire process if main thread is busy
Best workloadCPU-bound, long-lived requestsI/O-bound, many concurrent connections
Scaling modelVertical + horizontalCluster processes horizontally
📝

note

Node.js is not a silver bullet for every workload. CPU-intensive tasks such as video encoding, large matrix operations, or brute-force parsing should be moved to worker threads, child processes, or external services. Keeping the event loop free is the single most important performance rule in Node.js.
Your First HTTP Server

The http module is the foundation of every Node.js web framework. Understanding it helps you reason about request lifecycle, headers, backpressure, and error handling before adding abstraction layers like Express or Fastify.

first-server.js
JavaScript
1const http = require('http');
2
3const server = http.createServer((req, res) => {
4 if (req.url === '/health' && req.method === 'GET') {
5 res.writeHead(200, { 'Content-Type': 'application/json' });
6 res.end(JSON.stringify({ status: 'ok', uptime: process.uptime() }));
7 return;
8 }
9
10 res.writeHead(404, { 'Content-Type': 'application/json' });
11 res.end(JSON.stringify({ error: 'Not found' }));
12});
13
14server.listen(3000, () => {
15 console.log('Server listening on http://localhost:3000');
16});
17
18process.on('SIGTERM', () => {
19 server.close(() => {
20 console.log('Server closed gracefully');
21 process.exit(0);
22 });
23});

Notice the explicit return after handling a route. Without it, execution continues and you risk sending a second response, which throws ERR_HTTP_HEADERS_SENT. Also note the SIGTERM handler for graceful shutdown — production servers must finish in-flight requests before exiting.

best practice

Always set a Content-Type before ending the response. Omitting it causes browsers and API clients to guess the encoding, which can lead to security issues and inconsistent behavior.
Module Systems: CommonJS and ESM

Node.js supports two module systems. CommonJS uses require() and module.exports and has been the default since Node.js was created. ECMAScript Modules (ESM) use import and export and are the modern standard, aligning browser and server JavaScript.

commonjs-example.cjs
JavaScript
1// math.cjs (CommonJS)
2function add(a, b) {
3 return a + b;
4}
5module.exports = { add };
6
7// main.cjs
8const { add } = require('./math.cjs');
9console.log(add(2, 3));
esm-example.mjs
JavaScript
1// math.mjs (ESM)
2export function add(a, b) {
3 return a + b;
4}
5
6// main.mjs
7import { add } from './math.mjs';
8console.log(add(2, 3));

To use ESM by default, set "type": "module" in package.json. Alternatively, use the .mjs extension for individual files. Mixing the two systems in the same project requires careful attention to interop rules, top-level await, and __dirname availability.

warning

You cannot use require() inside an ESM module unless you create a custom createRequire(import.meta.url). Similarly, __dirname and __filename do not exist in ESM; use import.meta.dirname and import.meta.filename in Node.js 20.11+.
Topics Covered

The Node.js section is organized into focused guides that build from runtime fundamentals to production patterns. Start with the event loop and modules, then progress through core APIs and frameworks.

Why Node.js in 2026

Node.js 20 LTS and Node.js 22 bring meaningful improvements for production systems. The --experimental-strip-types flag supports native TypeScript execution, the built-in test runner is stable, and import.meta.dirname removes the need for path workarounds in ESM.

FeatureNode.js 20+Benefit
Built-in test runnernode --testNo test framework dependency for unit tests
Watch modenode --watchBuilt-in file watcher for development
Permission model--permissionRestrict filesystem and child process access
import.meta.dirnameNode 20.11+ESM equivalent of __dirname
Stable fetchGlobal fetchStandard HTTP client without node-fetch

info

If you are starting a new project in 2026, prefer ESM with "type": "module", use the built-in test runner for simple suites, and rely on fetch instead of third-party HTTP clients unless you need advanced features like proxies or connection pooling.
Common Pitfalls

Even experienced developers make these mistakes in Node.js. Being aware of them early prevents subtle bugs and production outages.

Blocking the event loop

Synchronous file reads, heavy JSON parsing, and CPU-bound loops on the main thread delay all concurrent requests. Use fs/promises, streams, or worker threads for large workloads.

Unhandled promise rejections

An unhandled rejection can crash the process in strict mode. Always attach catch() handlers or centralize error handling in middleware and event listeners.

Memory leaks from event listeners

Adding listeners without removing them causes objects to stay alive. Use once() for one-time events and call removeListener() when components are destroyed.

Hard-coded secrets

Never commit API keys or database URLs. Load configuration from environment variables and validate it at startup with a schema library like Zod.

$Blueprint — Engineering Documentation·Section ID: NODE-00·Revision: 1.0