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.
| Browser | Node |
|---|---|
window, document | global / globalThis, no DOM |
fetch, localStorage | fetch (since 18), fs, net, crypto |
| Sandboxed by origin | Full access to the machine it runs on |
| One tab, one user | One process, every user at once |
| Ships to the user | Runs on a machine you control |
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.
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
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
// 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
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.
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.