Runtimes
Node / Deno / Bun / edge2009 – todayJavaScript is no longer one host. The language spec is shared; the I/O, permissions, and module story are not — and that is where the real differences live.
The language has no file, socket, or HTTP APIs — every runtime invented its own. The same three lines of code look completely different in Node, Deno, and Bun, which is why code is rarely portable without a shim layer.
// Reading a file, three ways. None of these APIs exist in the ECMAScript
// spec — fetch is the only host API shared across every runtime.
const nodeJs = `import { readFile } from "node:fs/promises";
const text = await readFile("config.json", "utf8");`;
const deno = `const text = await Deno.readTextFile("config.json");`;
const bun = `const text = await Bun.file("config.json").text();`;
console.log("Node.js", nodeJs);
console.log("Deno", deno);
console.log("Bun", bun);
console.log("Common everywhere: fetch");| V8 | No I/O APIs at all — `fs`, `net`, `http`, even `console` and `setTimeout` are supplied by the host. The engine cannot open a file by itself. |
|---|---|
| Node.js | The original server stdlib: `fs`, `http`, `net`, `crypto`, `child_process` under the `node:` namespace. Callback-first heritage, promisified in `node:fs/promises`. |
| Deno | `Deno.*` globals (`Deno.readTextFile`, `Deno.serve`, `Deno.Kv`) plus a curated standard library on deno.land/std, all behind explicit permission flags. |
| Bun | `Bun.*` globals (`Bun.file`, `Bun.serve`, `Bun.sql`) plus drop-in Node API compatibility — a faster, batteries-included replacement. |