Skip to content

Node.js Runtime

beginner2 min read
  • 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

Related topics