The runtime

Understand what Node actually is, how it differs from the browser, and how a program starts.

01of 18
≈ 10Min read
6Parts

The same language, a different host

Node is the V8 engine — the one inside Chrome — plus a library of things a browser deliberately does not have: files, sockets, processes, operating-system access. The language is identical. Everything around it is different.

BrowserNode
window, documentglobal / globalThis, no DOM
fetch, localStoragefetch (since 18), fs, net, crypto
Sandboxed by originFull access to the machine it runs on
One tab, one userOne process, every user at once
Ships to the userRuns on a machine you control
This changes what a bug costs

A mistake in browser code affects one visitor. The same mistake on the server can expose every user’s data — which is why input validation and secrets management are lessons of their own later in this course.

Running a file

Use an LTS version in production. nvm or fnm lets you keep several installed and switch per project; a .nvmrc file records which one a project expects.

the whole toolchain, to begin with
node app.js              # run it
node --watch app.js      # re-run on save (Node 18.11+)
node --env-file=.env app.js   # load environment variables (20.6+)
node                     # a REPL, for trying one line

node --version           # v22.x — even numbers are the LTS releases

Globals worth knowing

what is in scope before you require anything
globalThis               // the global object
process                  // the running program: argv, env, exit, platform
console                  // writes to stdout / stderr, not to a devtools panel
setTimeout, setInterval  // the timers you already know
fetch, URL, AbortController, structuredClone   // web APIs, also here
__dirname, __filename    // CommonJS only — the folder and path of this file

process: arguments, environment, exit

the three you will use constantly
// node app.js build --watch
process.argv            // ["/path/node", "/path/app.js", "build", "--watch"]
const args = process.argv.slice(2);          // the ones you passed

process.env.NODE_ENV     // a string, or undefined — never a number, never a boolean
process.env.PORT ?? 3000

process.exit(1);         // non-zero means failure; CI reads this
process.on("SIGINT", () => { server.close(); process.exit(0); });   // Ctrl-C
Everything in env is a string

process.env.PORT is "3000", and process.env.DEBUG is "false" — a non-empty string, which is truthy. Convert deliberately: Number(...), value === "true".

One thread, and why it matters

Node runs your JavaScript on a single thread. Input and output — files, sockets, databases — happen elsewhere and hand the result back through the event loop, so thousands of connections can be in flight at once.

The consequence is the rule that governs every server you write: never block the thread. A synchronous file read, a two-second loop or a huge JSON.parse stops every other request in the process, not just the one you are serving.

the difference on a busy server
const data = fs.readFileSync("big.json");        // nobody else is served meanwhile
const data = await fs.promises.readFile("big.json");   // the loop keeps going

Where the code runs changes what you can trust

  • Anything the client sends is input, and input is hostile until validated.
  • Secrets live in the environment, never in the repository, and never reach the browser.
  • A crash takes the whole process down — with it, every request being served — so unhandled errors matter more here.

Practice