Node.js is the original server-side JavaScript runtime: V8 engine, libuv event loop, npm ecosystem origin. Not the fastest, not the trendiest — the safe default. Every npm package is tested against it; every cloud platform supports it; every regulated environment trusts it. When ecosystem maturity matters more than developer-loop ergonomics, Node is still the answer.
Node.js is a JavaScript runtime built on Chrome's V8 engine, designed for non-blocking I/O via the libuv event loop. The original 2009 thesis — "JavaScript on the server, with an event-driven async model that scales better than thread-per-request" — reshaped the backend industry. Sixteen years later, that thesis is so settled it's invisible: Node is the default JavaScript runtime, and "the npm registry" is, in practice, the JavaScript ecosystem.
The runtime ships with: V8 (the JS engine, shared with Chrome), libuv (the cross-platform async I/O library), and a curated set of native modules (fs, http, crypto, stream, worker_threads, cluster, etc.). Everything else — web frameworks, ORMs, test runners, bundlers, TypeScript compilers — comes from npm. Node is deliberately small at the core; the ecosystem is where the work happens.
The release cadence settles its own conservative rhythm: even-numbered releases (v18, v20, v22, v24) are LTS — supported for 30 months, the version most production deploys target. Odd-numbered releases (v19, v21, v23) are short-lived, used for previewing what the next LTS will adopt. As of 2026 the active LTS is v22; v24 ships LTS October 2026.
Single-threaded JavaScript main loop, multi-threaded I/O via libuv's worker pool, V8 garbage collection, npm + node_modules dependency tree, CommonJS or ESM module resolution, native add-ons via NAPI, optional worker_threads for CPU-bound parallelism. Has been the same shape for a decade; will be the same shape for another decade.
Most "Node knowledge" is really four things: how the event loop schedules work, how libuv multiplexes I/O, how the module system resolves imports, and how npm resolves dependency trees. Each is a small concept; together they account for almost everything that surprises a new Node developer.
The day-to-day commands:
# Install via nvm (recommended over system Node) curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash nvm install --lts # installs current LTS (v22 as of 2026) nvm use --lts # Run scripts node server.js # run a JS file node --watch server.js # auto-reload on change (built-in since v18) node --env-file=.env app.js # load .env (built-in since v20) # TypeScript — needs a compile/transpile step npx tsx server.ts # tsx for development (esm-friendly) npx ts-node server.ts # ts-node, the older default # Built-in test runner (since v18, stable since v22) node --test node --test --watch # Package management (npm bundled with Node) npm install # install dependencies npm run script-name # run package.json script npx some-cli # run a CLI from a package
The hello-world server, in vanilla Node:
// server.js — run with: node server.js import { createServer } from "node:http"; const server = createServer((req, res) => { if (req.url === "/") { res.writeHead(200, { "Content-Type": "text/plain" }); res.end("hello world"); } else { res.writeHead(404); res.end("not found"); } }); server.listen(3000);
Built-in fetch, WebSocket, and Web Streams (since v18). Built-in --watch mode (v18) and --env-file (v20). Native node:test runner (v18, stable v22). Native WebCrypto. Stable Permission Model (--experimental-permission in v20). Modern Node has caught up to a lot of what tooling used to provide externally. Worth re-reading the v22 changelog if you last did so before v18.
Node is the safe default, not a perfect runtime. Most of its rough edges are 16-year-old design decisions that became load-bearing — you can't fix them without breaking the world. Knowing which ones to expect saves a week of debugging.
Node supports both ECMAScript Modules (import) and CommonJS (require). The interop is mostly fine, occasionally surprising. Default export semantics differ; top-level await only works in ESM; __dirname and require aren't defined in ESM. Pick one mode per project where possible; if you must mix, read the Node docs on "exports" field carefully.
The "give every dependency its own copy of every dependency" model produces folder trees with hundreds of thousands of files. Most of the install-speed and disk-usage problems Bun / pnpm solve are about working around this. npm ci in CI helps; pnpm's content-addressable store helps more.
JavaScript runs on one thread. CPU-bound work blocks the event loop — everything stops, including incoming requests. Use worker_threads for CPU-bound tasks, or offload to a different process. The async I/O is non-blocking; the actual JS code is not.
Pre-NAPI, native addons (node-gyp compiled C++) broke between Node major versions. NAPI fixed most of this but legacy packages still exist. Always pin the addon's known-good Node version range; check the package's release notes when bumping Node majors.
Node's cold-start time (~30-50ms) plus your application's bootstrap (often 200ms-1s loading dependencies) makes serverless functions feel sluggish on first request. Keep dependency count down, use bundling for serverless deploys, consider Cloudflare Workers (V8 isolates have ~5ms cold start) for cold-start-sensitive workloads.
Despite years of demand, Node still doesn't run .ts files natively (v23 added --experimental-strip-types; not stable). Use tsx, ts-node, tsc --watch, or a bundler. This is the gap Bun and Deno specifically address.
"Just use Node" is unfashionable but usually correct. The cases where Node is genuinely the wrong choice are narrower than the discourse suggests — mostly cold-start-sensitive serverless and developer-ergonomics-first greenfield. For everything in the middle, Node's ecosystem maturity wins on the merits.
worker_threads, vm, deep cluster)Bun for local dev (fast install + fast test + native TypeScript), Node for production (mature long-tail + battle-tested). The package.json stays compatible; only the Bun-specific APIs (Bun.serve, Bun.write) need a fallback path. Many teams who started "Bun in production" experiments in 2024 settled on this hybrid by 2025 — it captures the developer-loop wins without staking production on a 3-year-old runtime. See tech/runtime/bun for the full case.
Node is the runtime layer beneath an enormous fraction of know.2nth.ai's content — Cloudflare Workers' Node compat, Frappe's bench, npm-installed CLIs across the tree, every Drizzle deployment. Where another runtime is mentioned (Bun, Deno, browser JS), Node is usually the alternative or the fallback.
Node's docs are comprehensive and well-organised; the changelog is the canonical place to learn what's new. The Node Foundation's release schedule and the LTS roadmap are the load-bearing artifacts for production planning.