The Node Runtime (& Deno, Bun)
Advanced · 18 min read · ▶ live playground · ✦ checkpoint
Node.js is JavaScript running outside the browser. The language is the same, but the runtime changes: instead of a page with window and document, your code runs as an operating-system process that can read files, open network connections, and stay alive waiting for work.
By the end of this lesson, Node should feel less like a separate language and more like JavaScript placed in a different room with different tools.
JavaScript, but not in a page
The browser gives JavaScript a world: the DOM, CSSOM, events, storage, location, history, Canvas, media APIs, and the DevTools console. Node gives JavaScript a different world: a command called node, a running process, access to the file system, networking, standard input and output, and built-in modules for server work.
That distinction matters more than the engine name. The core language features you have learned still apply: const, objects, arrays, classes, promises, async/await, modules, errors, and the event loop model. But the JavaScript engine does not provide the DOM. The runtime around the engine does.
Save this as hello-node.mjs:
console.log("Hello from Node.");
console.log(`Runtime: ${process.release.name}`);→ Hello from Node., then Runtime: node — process is a Node global that describes the running program.
Then run it from a terminal:
node hello-node.mjs
# Hello from Node.
# Runtime: nodeThe browser version of "run this JavaScript" was a <script> tag, a DevTools console, or a course playground. The Node version is a file plus the node command. That one shift is enough to turn JavaScript from "make this page interactive" into "write a command-line tool, an API server, a build script, a test runner, or a deployment job."
The server event loop is still the event loop
Lesson 10.2 gave you the rule: JavaScript has one call stack, and queued callbacks run only when the stack is empty. Node does not throw that model away. It uses the same basic shape on the server.
console.log("start");
setTimeout(() => {
console.log("timer callback");
}, 0);
Promise.resolve().then(() => {
console.log("promise job");
});
console.log("end");→ start, end, promise job, timer callback — synchronous code first, then microtasks, then timer callbacks.
The server twist is what the runtime waits on. In the browser, the runtime waits on clicks, timers, network responses, animation frames, and page lifecycle events. In Node, it waits on timers, file operations, network sockets, HTTP requests, database responses, child processes, and streams. Your JavaScript still runs one piece at a time. The runtime does the waiting and queues your callback or promise continuation when a result is ready.
That is why Node can handle many slow requests without creating one JavaScript thread per request. While one database call or file read is waiting, the stack is free for other work. When the wait finishes, Node queues the next bit of JavaScript. CPU-heavy loops are still dangerous for the same reason they freeze a browser page: they occupy the one stack and keep the loop from reaching queued work.
What exists in Node but not the browser
A quick runtime check makes the boundary visible:
const names = ["document", "window", "process", "URL", "console"];
for (const name of names) {
console.log(`${name}: ${typeof globalThis[name]}`);
}→ document: undefined, window: undefined, process: object, URL: function, console: object.
globalThis is the standard name for "the global object in this runtime." In a browser page, globalThis is window. In Node, globalThis is Node's global object, and it does not have a DOM hanging off it.
Node-specific APIs usually arrive in two forms:
- Globals, such as
process,console, timers, andBuffer. - Built-in modules, imported with specifiers like
node:os,node:path,node:http, andnode:fs/promises.
The node: prefix is a useful signal: this module is shipped with Node itself. You do not install it from npm.
import { platform, uptime } from "node:os";
console.log(process.release.name);
console.log(platform() === process.platform);
console.log(typeof uptime());→ node, then true, then number — node:os is a built-in module, and process describes the current Node process.
You do not need to memorize the built-in list today. You need the categories. node:fs/promises works with files. node:path works with paths. node:http can build an HTTP server without a framework. node:crypto handles hashing and randomness. node:child_process starts other programs. Those names are Node's server-side toolkit, the way document, addEventListener, and localStorage were the browser toolkit.
The module story in Node
Modern course code uses ES modules. In Node, the clearest ways to opt into ESM are:
{
"type": "module"
}or a file extension like .mjs. That is why the examples above use hello-node.mjs.
Once Node knows a file is ESM, imports look like the module imports from Section 19:
import { platform } from "node:os";for a Node built-in.import express from "express";for an npm package installed in the project.import { formatMoney } from "./money.js";for one of your own files.
You will still see CommonJS in older Node code and some configuration files: const fs = require("fs"); and module.exports = .... Read it as Node history with a long tail, not as the style to start with. New examples in this course use ESM unless a tool specifically requires otherwise.
Deno and Bun: same language, different runtime choices
Node is the default server runtime in this course because it is the center of the JavaScript server and tooling ecosystem. It is not the only runtime.
In the current 2026 landscape, Node 24 is the Active LTS line and Node 26 is Current. Deno 2 is npm-compatible, which means it can work with much of the npm package ecosystem. Bun 1.2+ is stable for serious app work. All three run JavaScript, but their built-in APIs, command-line tools, compatibility details, and deployment assumptions are not identical.
So treat "Node, Deno, or Bun?" as a runtime choice, not a language choice. The language skills transfer. The runtime APIs are what you check before copying code between them.
Next, you will use Node for the first server-side jobs most scripts need: reading files, handling paths portably, receiving command-line input, reading environment variables, and exiting with useful status codes.
Checkpoint
Answer all three to mark this lesson complete