Node Setup & Runtime
Install Node 24, fnm, what V8 + libuv actually do.
For 12 years, JavaScript only ran inside the browser. Then in 2009, Ryan Dahl ripped the V8 engine out of Chrome, glued it to a C library for non-blocking I/O, and called it Node. Suddenly the language you used to animate buttons could also write files, listen on sockets, and host servers. Today, almost every backend you'll touch in a JS shop runs on Node, Bun, or Deno. Let's set it up.
What is Node, really?
Node is two big things bolted together, plus a standard library:
- V8 - Google's JavaScript engine. Same one in Chrome. Compiles your JS to native code on the fly.
- libuv - a C library that gives V8 access to the OS: file system, network sockets, timers, child processes. Also runs the event loop.
- Node's built-in modules -
fs,http,path,crypto, and dozens more.
V8 alone can't open a file. The browser doesn't let it. Node is the runtime that hands V8 the OS keys.
The event loop in 30 seconds
You'll get a proper chapter on async later. The 30-second version: Node runs your synchronous code top to bottom. Anything async (a network call, a file read, a setTimeout) gets handed to libuv. When it finishes, its callback joins a queue. The event loop picks the next callback off the queue when the main stack is empty.
console.log("1");
setTimeout(() => console.log("3"), 0);
Promise.resolve().then(() => console.log("2"));
console.log("4");
// Output: 1, 4, 2, 3
// Sync runs first. Then microtasks (promises). Then macrotasks (timers).The order isn't magic. Microtasks (promise callbacks) drain before the next macrotask (timers, I/O). Once you internalize this, async bugs stop being mysterious.
Installing Node: don't use the .pkg installer
Downloading the Node installer from nodejs.org works once. Then version 26 ships, your project needs 24, an old contract job still needs 22, and you're stuck. Use a version manager from day one.
Two good options in 2026. Pick one:
Option A: fnm (Fast Node Manager)
# macOS
brew install fnm
# Linux/macOS via curl
curl -fsSL https://fnm.vercel.app/install | bash
# Add to ~/.zshrc or ~/.bashrc:
eval "$(fnm env --use-on-cd)"
# Install the latest LTS
fnm install --lts
fnm use lts-latest
fnm default lts-latest
node --version
# v24.x.xOption B: Volta
# macOS / Linux
curl https://get.volta.sh | bash
# Install Node + pin per-project
volta install node@lts
cd my-project
volta pin node@24
# Volta reads the pinned version from package.json automatically.nvmrc or .node-version file. Compatible with the old nvm ecosystem.Volta pins the Node version inside
package.json so it travels with the repo automatically. Better for teams.Node's LTS cadence
Node has released a new major every 6 months, where even-numbered versions become LTS (Long-Term Support) in October of their release year and get maintained for about 30 months. Odd-numbered versions are short-lived and never LTS, so they do not belong in production.
- Node 24 ("Krypton") - Active LTS since 28 October 2025, supported until 30 April 2028. Use this for new projects.
- Node 26 - released 5 May 2026. Still the Current line; it becomes LTS on 28 October 2026. Fine for experiments, not for a production deploy you have to babysit.
- Node 22 ("Jod") - in maintenance since October 2025, security fixes only until 30 April 2027.
- Node 20 - end of life 30 April 2026. Already dead.
- Node 18 - end of life 30 April 2025. Dead longer.
Hello, Node
Make a file, run it. That's the whole loop.
mkdir hello-node && cd hello-node
echo 'console.log("Hello, " + process.version)' > hi.js
node hi.js
# Hello, v24.0.0Or skip the file and use the REPL:
node
> const x = 21
> x * 2
42
> .exitNode runs TypeScript now
This used to need ts-node or a build step. As of Node 22.18 and 23.6 it is on by default, and it was declared stable in Node 24.12. Point Node at a .ts file and it runs.
echo 'const n: number = 42; console.log(n);' > hi.ts
node hi.ts
# 42What Node does is type stripping, not compiling. It replaces the type annotations with whitespace and runs the result. Nothing is type-checked. That distinction explains every limitation:
- No type checking at all. A type error runs happily. You still run
tsc --noEmitin CI. - It ignores
tsconfig.json. No path aliases, no downleveling. - Syntax that needs real codegen errors out:
enum,namespacewith runtime code, parameter properties, and decorators. Node will not polyfill them. import typeis mandatory for type-only imports. Without the keyword Node treats it as a value import and you get a runtime error. Turn onverbatimModuleSyntaxso TypeScript catches this for you.- Extensions are required in specifiers:
import "./file.ts". And.tsxis not supported.
tsx rather than ts-node: npm i -D tsx, then npx tsx file.ts or node --import=tsx file.ts.Bun and Deno: the other two runtimes
Node has competition now. Both run JavaScript on the server. Both have wildly different design philosophies.
Bun
Built on JavaScriptCore (Safari's engine) instead of V8. Written in Zig. Goals: be a drop-in Node replacement that's 2-5x faster, with a built-in bundler, test runner, and TypeScript support out of the box. It hit 1.0 in September 2023 and is still on the 1.x line, now with a built-in S3 client and Postgres client too.
curl -fsSL https://bun.sh/install | bash
# Bun runs Node code mostly unchanged
bun hi.js
# It also installs packages
bun install
bun add zodDeno
Same Ryan Dahl who wrote Node, returning a decade later to fix his own regrets. Secure by default (you must opt into file/network access), TypeScript native, ships a standard library, uses Web APIs like fetch everywhere. Deno 2 arrived in late 2024 and the pitch shifted with it: Deno now markets itself as a drop-in runtime for Node developers, npm packages and all, rather than a separate ecosystem you migrate to.
# macOS / Linux
curl -fsSL https://deno.land/install.sh | sh
# Run a file with explicit permissions
deno run --allow-net server.tsQuiz
What two main pieces make up the Node runtime?
Recap
- Node = V8 + libuv + a standard library. V8 executes JS; libuv talks to the OS.
- The event loop juggles async work on a single thread. Microtasks (promises) run before macrotasks (timers).
- Install via fnm or Volta. Never use the raw installer.
- Use the Active LTS version. That is Node 24 until Node 26 takes over in October 2026.
- Bun = fast, batteries-included. Deno = secure, Web-Standards-first. Node still wins on ecosystem.