Node.js Runtime
- Node.js
- JavaScript
How Node.js runs JavaScript outside the browser on the V8 engine, using a single-threaded event loop and non-blocking I/O to handle many requests at once.
What you'll understand
- What Node.js is and why it lets JavaScript run on a server
- What the event loop is and why Node is called non-blocking
- How a single thread can serve many requests at once
Explanation
Node.js is a runtime that lets you run JavaScript outside the browser, most often on a server. It bundles the V8 engine, the same one that powers Chrome, together with libraries for things a browser never needed: reading files, opening network sockets, and talking to databases. That combination is what turns a browser language into a general backend language.
The defining feature of Node is its event loop. Node runs your JavaScript on a single main thread, but instead of waiting for slow work like a database query or a file read to finish, it hands that work off and keeps going. When the work completes, Node runs the callback you registered. This is what non-blocking means: the thread is almost never sitting idle waiting.
This model is why a single Node process can handle thousands of concurrent connections. Most server work is I/O, waiting on the network, disk, or database, not heavy computation. Node excels there. The flip side is that a long, synchronous computation blocks the one thread and stalls every other request, so CPU-heavy work needs care.
Examples
This TypeScript example shows non-blocking I/O: the second log runs before the file finishes reading, because the read does not block the thread.
import { readFile } from 'node:fs';
readFile('data.txt', 'utf8', (err, contents) => {
if (err) throw err;
console.log('file loaded:', contents.length, 'chars');
});
console.log('this logs first, while the file is still loading');The same read with async/await reads top to bottom while staying non-blocking under the hood:
import { readFile } from 'node:fs/promises';
const contents = await readFile('data.txt', 'utf8');
console.log('file loaded:', contents.length, 'chars');Common mistakes
- Blocking the event loop with heavy synchronous work, which freezes every other request.
- Assuming Node runs your callbacks in parallel; your JavaScript runs one piece at a time on one thread.
- Using synchronous file or crypto functions (the Sync variants) in a request path.
- Expecting browser globals like window or document; Node has its own globals instead.
Best practices
- Prefer asynchronous, non-blocking APIs (promises or callbacks) for I/O.
- Keep CPU-heavy work off the main thread, for example with worker threads or a separate service.
- Use a current Long Term Support (LTS) version of Node for stability.
- Lean on the built-in node: modules before reaching for a dependency.
Further reading
- Node.js, Introduction to Node.js — https://nodejs.org/en/learn/getting-started/introduction-to-nodejs
- Node.js, The event loop — https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick