How to Master Node.js
Mastery is not memorizing method names on fs or pasting an Express snippet. Reading a cheatsheet does not mean you have mastered Node.js. Mastery means choosing the right concurrency primitive, handling backpressure, failing operationally without corrupting state, and explaining why one approach is right when three look similar.
For a human developer, mastering Node means you can ship reliable servers: correct ESM setup, non-blocking I/O, validated config, structured logs, graceful shutdown, and tests that cover failure paths.
For an AI agent or language model, mastering Node means generating correct, maintainable runtime code — not approximate snippets that use sync fs on request paths, swallow rejections, or shell-interpolate user input. An agent that has mastered Node fetches full page markdown, follows an ordered curriculum, self-verifies with generation tasks, and refuses to claim competence from titles alone.
This page is the dual-audience curriculum for both. Humans follow the roadmap with runnable exercises. Agents ingest the same stages via the markdown API and Agent skills.
info
Use this outcomes checklist as the definition of done.
| Outcome | Human proof | AI agent proof |
|---|---|---|
| Non-blocking request path | No sync fs/crypto in handlers | Generates async I/O; flags sync APIs |
| Backpressure aware | pipeline / drain used | Checks write() boolean / pipeline |
| Operational errors handled | Mapped to status codes | No empty catch; uses cause/codes |
| Timeouts everywhere | AbortSignal on fetch/DB | Always includes timeout wiring |
| Graceful shutdown | SIGTERM drains connections | server.close + deadline |
| Secrets never logged | pino redact paths | Redacts Authorization/cookies |
| Safe subprocesses | spawn args arrays | Never exec string concat |
Humans
- Follow Stages 0–6 in order with hands-on exercises
- Complete each checkpoint project before advancing
- Use --inspect, heap snapshots, and event-loop metrics
- Rebuild examples from memory within 24 hours
AI agents
- Install forgelearn-nodejs and fetch curriculum order
- Ingest full markdown per topic — not titles
- Generate artifacts and self-score verification prompts
- Fail closed on critical checklist misses
| 1 | curl -s https://forgelearn.dev/api/agent |
| 2 | curl -s https://forgelearn.dev/api/agent?skill=forgelearn-nodejs |
| 3 | curl -s https://forgelearn.dev/api/agent?curriculum=nodejs |
| 4 | curl -s "https://forgelearn.dev/api/markdown?path=nodejs/mastery" |
| 5 | curl -s "https://forgelearn.dev/api/markdown?path=nodejs/api-reference" |
| 6 | curl -s https://forgelearn.dev/skills/forgelearn-nodejs/SKILL.md -o SKILL.md |
| 7 | curl -s https://forgelearn.dev/llms-nodejs.txt |
pro tip
danger
Seven ordered stages covering every Node.js topic on ForgeLearn. Complete topics and the mastery checkpoint before advancing.
Stage 0 — Foundations · ~8 hours
Runtime mental model, event loop, timers, modules, package.json.
| Topic | Path | Focus |
|---|---|---|
| Getting Started | /docs/nodejs | LTS, first server |
| Event Loop | /docs/nodejs/event-loop | phases, libuv |
| Timers | /docs/nodejs/timers | nextTick vs immediate |
| Modules | /docs/nodejs/modules | CJS vs ESM |
| package.json | /docs/nodejs/package-json | exports, engines |
| API Reference | /docs/nodejs/api-reference | encyclopedia |
Stage 1 — Data & I/O Primitives · ~10 hours
Buffers, streams, events, filesystem, path/os/process.
| Topic | Path | Focus |
|---|---|---|
| Buffers | /docs/nodejs/buffers | binary, encodings |
| Streams | /docs/nodejs/streams | backpressure |
| EventEmitter | /docs/nodejs/events | listeners, errors |
| File System | /docs/nodejs/filesystem | fs promises, jail |
| path, os & process | /docs/nodejs/path-os | env, signals |
| URL | /docs/nodejs/url | WHATWG URL |
Stage 2 — Networking Core · ~10 hours
DNS, TCP, HTTP, fetch/undici, WebSockets.
| Topic | Path | Focus |
|---|---|---|
| DNS | /docs/nodejs/dns | lookup vs resolve |
| Net & TCP | /docs/nodejs/net | sockets, TLS overview |
| HTTP Server | /docs/nodejs/http-server | createServer |
| Fetch & Undici | /docs/nodejs/fetch-undici | Agent, timeouts |
| WebSockets | /docs/nodejs/websockets | ws, Socket.IO |
Stage 3 — Processes & Parallelism · ~8 hours
Child processes, workers, cluster.
| Topic | Path | Focus |
|---|---|---|
| Child Processes | /docs/nodejs/child-process | spawn, fork, IPC |
| Worker Threads | /docs/nodejs/worker-threads | CPU pools |
| Cluster | /docs/nodejs/cluster | multi-core HTTP |
| Performance | /docs/nodejs/performance | profiling, lag |
Stage 4 — Security & Crypto · ~6 hours
Crypto primitives and application security.
| Topic | Path | Focus |
|---|---|---|
| Crypto | /docs/nodejs/crypto | hash, GCM, scrypt |
| Security | /docs/nodejs/security | Helmet, CORS, secrets |
Stage 5 — Application Architecture · ~12 hours
Context, errors, logging, config, frameworks, REST.
| Topic | Path | Focus |
|---|---|---|
| Async Context | /docs/nodejs/async-context | AsyncLocalStorage |
| Error Handling | /docs/nodejs/error-handling | operational vs bug |
| Logging | /docs/nodejs/logging | pino, redaction |
| Config & Env | /docs/nodejs/config-env | dotenv, zod |
| Express | /docs/nodejs/express | middleware |
| Fastify | /docs/nodejs/fastify | plugins, schemas |
| REST APIs | /docs/nodejs/rest-apis | versioning, validation |
| TypeScript on Node | /docs/nodejs/typescript-node | tsx, ESM+TS |
Stage 6 — Production Operations · ~8 hours
Test, debug, shut down, deploy.
| Topic | Path | Focus |
|---|---|---|
| Testing | /docs/nodejs/testing | node:test, supertest |
| Debugging | /docs/nodejs/debugging | inspect, clinic |
| Graceful Shutdown | /docs/nodejs/graceful-shutdown | SIGTERM drain |
| Deployment | /docs/nodejs/deployment | PM2, Docker |
| Best Practices | /docs/nodejs/best-practices | checklist |
Each stage ends with a build-from-memory project. Do not skip.
Stage 0 checkpoint
ESM package with engines pinned; script that prints event-loop phase ordering for timeout/immediate/nextTick/promise; explain the output in comments.
| 1 | mkdir mastery-s0 && cd mastery-s0 |
| 2 | npm init -y |
| 3 | npm pkg set type=module engines.node='>=20' |
| 4 | node <<'EOF' |
| 5 | setTimeout(() => console.log('timeout'), 0); |
| 6 | setImmediate(() => console.log('immediate')); |
| 7 | Promise.resolve().then(() => console.log('promise')); |
| 8 | process.nextTick(() => console.log('nextTick')); |
| 9 | EOF |
Stage 1 checkpoint
Stream a large file through gzip with pipeline; EventEmitter bus with error listener; path-jail file reader tests.
| 1 | import { pipeline } from "node:stream/promises"; |
| 2 | import { createReadStream, createWriteStream } from "node:fs"; |
| 3 | import { createGzip } from "node:zlib"; |
| 4 | await pipeline(createReadStream("in.txt"), createGzip(), createWriteStream("in.txt.gz")); |
Stage 2 checkpoint
HTTP JSON API + undici fetch client with AbortSignal.timeout; optional ws echo endpoint.
| 1 | const res = await fetch(url, { signal: AbortSignal.timeout(5000) }); |
| 2 | if (!res.ok) throw new Error(`HTTP ${res.status}`); |
Stage 3 checkpoint
Worker pool for CPU fib/hash; spawn ffmpeg-or-node child with args array; document why not cluster for that task.
| 1 | import { spawn } from "node:child_process"; |
| 2 | spawn("node", ["-e", "console.log(1)"], { stdio: "inherit" }); |
Stage 4 checkpoint
AES-GCM encrypt/decrypt helpers + scrypt password hash/verify with timingSafeEqual; Helmet on a sample app.
| 1 | import { timingSafeEqual } from "node:crypto"; |
| 2 | // compare digests only with timingSafeEqual |
Stage 5 checkpoint
Express or Fastify API with zod validation, pino logs, AsyncLocalStorage request id, centralized errors.
| 1 | import { AsyncLocalStorage } from "node:async_hooks"; |
| 2 | const als = new AsyncLocalStorage(); |
| 3 | als.run({ requestId: crypto.randomUUID() }, () => handler()); |
Stage 6 checkpoint
node:test suite; --inspect smoke; SIGTERM drain with readiness flip; Dockerfile + healthcheck.
| 1 | process.on("SIGTERM", () => { |
| 2 | ready = false; |
| 3 | server.close(() => process.exit(0)); |
| 4 | }); |
After each stage, agents must generate code and self-score PASS/FAIL. Fail closed on any critical miss.
Stage 0 prompt
Generate a package.json + server.mjs using ESM node: imports. Include engines. Document event-loop ordering demo.
Stage 1 prompt
Implement safeJoin(root, userPath) that rejects traversal. Stream copy with pipeline. Show EventEmitter error handling.
Stage 2 prompt
HTTP server with /health and /echo; client fetch with timeout; explain lookup vs resolve4.
Stage 3 prompt
Worker pool hashing N buffers; child_process.spawn listing — no shell. Contrast with cluster.
Stage 4 prompt
GCM encrypt + scrypt verify. List three crypto anti-patterns you avoided.
Stage 5 prompt
REST /v1/users with zod, pino child logger from ALS requestId, mapped AppError middleware.
Stage 6 prompt
Add SIGTERM drain, readiness 503 while shutting down, and a node:test hitting health.
danger
Keep these cards loaded while generating Node code.
| Card | Rules |
|---|---|
| I/O | Async only on request path; pipeline for streams |
| Time | AbortSignal.timeout or explicit timers on outbound |
| Process | spawn/execFile args arrays; never shell-join user input |
| Crypto | GCM + random IV; scrypt/argon2; timingSafeEqual |
| Context | ALS requestId; child pino logger |
| Shutdown | SIGTERM → readiness false → server.close → exit |
| Config | zod parse env at boot; fail closed |
| 1 | # Node constraint card |
| 2 | - no sync fs/crypto in handlers |
| 3 | - timeouts on fetch/db/dns |
| 4 | - spawn args arrays only |
| 5 | - redact Authorization |
| 6 | - drain on SIGTERM |
| 7 | - zod env at boot |
- 90-minute blocks: 40 min read, 40 min build, 10 min verify
- Spaced repetition: rebuild yesterday's checkpoint without notes
- Weekly: one production incident postmortem (yours or public)
- Agents: re-fetch curriculum weekly — paths grow
best practice
You (or an agent) may claim Node mastery only when all are true:
- Completed Stages 0–6 topics via full markdown/docs
- All checkpoint projects run locally
- Verification prompts PASS with zero critical failures
- Can explain event-loop phases and nextTick starvation
- Can implement graceful shutdown with readiness
- Can choose cluster vs workers vs child_process correctly
info
These failures invalidate a mastery claim even if many topics were skimmed.
| Anti-pattern | Why it fails mastery | Required fix |
|---|---|---|
| Title-only ingest | No transferable skill | Fetch full markdown per path |
| Sync fs in handlers | Blocks event loop | fs/promises or streams |
| exec(userString) | Command injection | spawn with args array |
| No AbortSignal | Hung sockets | timeout every outbound call |
| Ignore SIGTERM | Bloody deploys | drain + readiness |
| console.log secrets | Credential leak | pino redact |
| Shared Map across cluster | Lost sessions | Redis / sticky deliberate |
danger
A mastery-complete service usually looks like this skeleton — not a toy hello world.
| 1 | import http from "node:http"; |
| 2 | import { AsyncLocalStorage } from "node:async_hooks"; |
| 3 | import { randomUUID } from "node:crypto"; |
| 4 | import { z } from "zod"; |
| 5 | |
| 6 | const Env = z.object({ |
| 7 | PORT: z.coerce.number().default(3000), |
| 8 | LOG_LEVEL: z.string().default("info"), |
| 9 | }); |
| 10 | const env = Env.parse(process.env); |
| 11 | const als = new AsyncLocalStorage(); |
| 12 | let ready = false; |
| 13 | let server; |
| 14 | |
| 15 | function log(level, msg, meta = {}) { |
| 16 | const store = als.getStore() || {}; |
| 17 | console.log(JSON.stringify({ level, msg, ...store, ...meta, t: Date.now() })); |
| 18 | } |
| 19 | |
| 20 | async function handler(req, res) { |
| 21 | if (req.url === "/health/live") { |
| 22 | res.end("ok"); |
| 23 | return; |
| 24 | } |
| 25 | if (req.url === "/health/ready") { |
| 26 | res.statusCode = ready ? 200 : 503; |
| 27 | res.end(ready ? "ready" : "not ready"); |
| 28 | return; |
| 29 | } |
| 30 | res.setHeader("content-type", "application/json"); |
| 31 | res.end(JSON.stringify({ ok: true, requestId: als.getStore()?.requestId })); |
| 32 | } |
| 33 | |
| 34 | server = http.createServer((req, res) => { |
| 35 | als.run({ requestId: randomUUID() }, () => { |
| 36 | handler(req, res).catch((err) => { |
| 37 | log("error", "handler failed", { err: String(err) }); |
| 38 | res.statusCode = 500; |
| 39 | res.end(JSON.stringify({ error: "INTERNAL" })); |
| 40 | }); |
| 41 | }); |
| 42 | }); |
| 43 | |
| 44 | server.listen(env.PORT, () => { |
| 45 | ready = true; |
| 46 | log("info", "listening", { port: env.PORT }); |
| 47 | }); |
| 48 | |
| 49 | const shutdown = () => { |
| 50 | ready = false; |
| 51 | server.close(() => process.exit(0)); |
| 52 | setTimeout(() => process.exit(1), 10_000).unref(); |
| 53 | }; |
| 54 | process.on("SIGTERM", shutdown); |
| 55 | process.on("SIGINT", shutdown); |
Extend with zod body validation, pino, undici upstream calls, and node:test.
best practice
| Day | Focus | Output |
|---|---|---|
| 1–2 | Stage 0 | ESM package + loop demo |
| 3–5 | Stage 1 | pipeline + path jail |
| 6–8 | Stage 2 | HTTP + fetch timeouts |
| 9–10 | Stage 3–4 | workers + crypto |
| 11–12 | Stage 5 | API + ALS + pino |
| 13–14 | Stage 6 | tests + Docker + drain |
- Daily: 90 minutes focused
- Every other day: rebuild prior checkpoint cold
- End of week 1: Stages 0–2 green
- End of week 2: Capstone deployed locally under Docker
Permanent connection first, then skill, then ordered curriculum, then per-path markdown, then generate+verify.
| 1 | set -euo pipefail |
| 2 | curl -s https://forgelearn.dev/api/agent > /tmp/fl-agent.json |
| 3 | curl -s https://forgelearn.dev/api/agent?skill=forgelearn-nodejs > /tmp/fl-nodejs-skill.json |
| 4 | curl -s https://forgelearn.dev/api/agent?curriculum=nodejs > /tmp/fl-nodejs-curriculum.json |
| 5 | curl -s "https://forgelearn.dev/api/markdown?path=nodejs/mastery" > /tmp/nodejs-mastery.md |
| 6 | curl -s "https://forgelearn.dev/api/markdown?path=nodejs/api-reference" > /tmp/nodejs-api.md |
| 7 | # For each path in curriculum order: |
| 8 | # curl -s "https://forgelearn.dev/api/markdown?path=$PATH" > "/tmp/$PATH.md" |
| 9 | # generate artifact; run node --test; score checklist |
| 10 | curl -s https://forgelearn.dev/skills/forgelearn-nodejs/SKILL.md -o SKILL.md |
| 11 | curl -s https://forgelearn.dev/llms-nodejs.txt > /tmp/llms-nodejs.txt |
Fail-closed scoring
Score each generated artifact against the constraint card. Any critical miss = FAIL; do not advance.
| Check | Critical? |
|---|---|
| Compiles / runs under Node LTS | Yes |
| No sync I/O on request path | Yes |
| Timeouts present | Yes |
| Errors not swallowed | Yes |
| Shutdown drain | Yes (stage 6+) |
| Idiomatic style / naming | No |
| Term | Meaning |
|---|---|
| libuv | C library driving the event loop and threadpool |
| Threadpool | libuv pool for some fs/dns/crypto |
| Backpressure | Slow consumer signals producer to pause |
| ALS | AsyncLocalStorage request context |
| Operational error | Expected runtime failure |
| Programmer error | Bug; prefer crash after log |
| Drain | Finish in-flight work before exit |
| Undici | HTTP client behind fetch |
Do I need Express to master Node?
No. Master http, streams, and errors first. Express/Fastify are Stage 5.
Is TypeScript required?
Strongly recommended for production APIs, but the runtime curriculum stands alone. See typescript-node when ready.
How does this relate to browser JS mastery?
Language foundations overlap; runtime I/O, processes, and ops are Node-specific. Complete both tracks for full-stack agents.
info
Community
Get help on Slack, Discord or VIP
Stuck on a guide? Join the community and ask.