webdev.complete
🟢 Node.js & npm
🟢The Backend
Lesson 65 of 117
20 min

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.

Why "non-blocking" matters
A traditional server spawns one thread per request. 10,000 connections = 10,000 threads = your kernel sweating. Node uses onethread and an event loop that juggles thousands of I/O operations at once. That's the whole pitch.

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.

js
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 20 ships, your project needs 22, another project still needs 18, and you're stuck. Use a version manager from day one.

Two good options in 2026. Pick one:

Option A: fnm (Fast Node Manager)

bash
# 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.x

Option B: Volta

bash
# 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
fnm vs Volta
fnm is fast and uses a .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 releases a new major version every 6 months. Even-numbered versions become LTS (Long-Term Support) in October of their release year and get maintained for ~30 months. Odd-numbered versions are short-lived; never use them in production.

  • Node 24 - released April 2026, becomes LTS October 2026. Use this for new projects.
  • Node 22 - current LTS until April 2027. Safe choice.
  • Node 20 - maintenance LTS until April 2026. End of life soon.
  • Node 18 - end of life April 2025. Dead. Move off.

Hello, Node

Make a file, run it. That's the whole loop.

bash
mkdir hello-node && cd hello-node
echo 'console.log("Hello, " + process.version)' > hi.js
node hi.js
# Hello, v24.0.0

Or skip the file and use the REPL:

bash
node
> const x = 21
> x * 2
42
> .exit

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. Released 1.0 in September 2023.

bash
curl -fsSL https://bun.sh/install | bash

# Bun runs Node code mostly unchanged
bun hi.js

# It also installs packages
bun install
bun add zod

Deno

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 (late 2024) is now npm-compatible too.

bash
# macOS / Linux
curl -fsSL https://deno.land/install.sh | sh

# Run a file with explicit permissions
deno run --allow-net server.ts
Which should you pick?
For learning, stick with Node. It has the most jobs, the most tutorials, the most StackOverflow answers, and 99% of the npm ecosystem assumes it. Try Bun for the speed boost on personal projects. Try Deno when you want secure scripts or care about a Web-Standards-first runtime.

Quiz

Quiz1 / 4

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 LTS version. Node 22 is current LTS; Node 24 becomes LTS in October 2026.
  • Bun = fast, batteries-included. Deno = secure, Web-Standards-first. Node still wins on ecosystem.