Skip to content
Node.js nd getting-started 4 min read

What is Node.js?

Node.js is an open-source JavaScript runtime that lets you run JavaScript outside the browser — on servers, in containers, and on your own machine as command-line tools. It pairs Google’s high-performance V8 engine with a small C++ core (libuv) that provides non-blocking I/O, so a single Node process can handle thousands of concurrent connections without spawning a thread per request. That combination is why Node powers a huge share of today’s APIs, build tooling, and real-time applications.

Where Node.js came from

Node.js was created by Ryan Dahl in 2009. At the time, most web servers handled each connection with a dedicated thread or process, which scaled poorly under many slow, concurrent clients. Dahl’s insight was to take the same V8 engine that made JavaScript fast in Chrome and wrap it in an event loop that never blocks on I/O. Instead of waiting for a database query or file read to finish, Node registers a callback and moves on, processing results as they arrive.

The project grew rapidly, gained the npm package registry, and is now stewarded by the OpenJS Foundation with a predictable release schedule. Modern versions (Node 20 and 22 LTS) ship with native ECMAScript modules, a built-in fetch, a stable test runner, and a node: prefix for core modules.

The non-blocking, event-driven model

The heart of Node is the event loop: a single-threaded loop that picks up completed I/O events and runs their callbacks. Because I/O is offloaded to the operating system (or a small background thread pool in libuv), the main thread stays free to keep accepting work. You write code that looks sequential with async/await, but under the hood Node is juggling many operations at once.

import { readFile } from "node:fs/promises";

console.log("1. Start");

const data = await readFile("config.json", "utf8");
console.log("3. File contents:", data.trim());

console.log("2. This logs before the file in callback style");

The synchronous console.log calls run immediately, while readFile returns a promise that resolves later. With await the function pauses at line 5 without blocking the rest of the runtime — other timers, sockets, and requests keep being serviced.

Tip: “Single-threaded” refers to your JavaScript callbacks running on one thread. Network and file I/O happen in the background, which is exactly why Node excels at I/O-heavy workloads but should hand off CPU-bound work to worker threads or separate processes.

A minimal HTTP server

Because non-blocking I/O is built in, a fully functional web server fits in a few lines using the core node:http module — no framework required.

import { createServer } from "node:http";

const server = createServer((req, res) => {
  res.writeHead(200, { "Content-Type": "application/json" });
  res.end(JSON.stringify({ message: "Hello from Node.js", url: req.url }));
});

server.listen(3000, () => {
  console.log("Server running at http://localhost:3000");
});

Run it and request the endpoint:

node server.js
curl http://localhost:3000/status

Output:

Server running at http://localhost:3000
{"message":"Hello from Node.js","url":"/status"}

Common use cases

Node’s strengths cluster around tasks that spend most of their time waiting on I/O rather than crunching numbers.

Use caseWhy Node fits
REST and GraphQL APIsLightweight, fast JSON handling and a vast package ecosystem
Real-time appsWebSockets and the event loop handle many open connections cheaply
CLI tools and scriptsOne runtime for both your app and your tooling
Build toolingBundlers, linters, and test runners (Vite, ESLint, Prettier) run on Node
Backends-for-frontendsShare types and logic between server and browser JavaScript

How Node differs from browser JavaScript

Both environments run the same JavaScript language on V8, but the surrounding APIs differ because the goals differ. The browser exposes the DOM, window, and localStorage; Node exposes the file system, processes, networking, and operating-system details.

Browser JavaScriptNode.js
window, document, DOM APIsprocess, Buffer, globalThis
No direct file system accessnode:fs for reading and writing files
Modules via <script type="module">ES modules (import) and CommonJS (require)
Sandboxed by the browserFull OS access (files, env vars, child processes)
fetch, localStorage, alertfetch (built in since Node 18), no DOM

ES modules are the modern default; opt in with "type": "module" in package.json or a .mjs extension. CommonJS (require/module.exports) remains common in older packages, and Node interoperates with both.

// ES module (modern default)
import { hostname } from "node:os";

// CommonJS (still widely used)
const { hostname } = require("node:os");

Best practices

  • Target an active LTS release (Node 20 or 22) for production — LTS lines get long-term security and bug fixes.
  • Prefer ES modules and async/await for new projects; they are the modern, framework-agnostic default.
  • Use the node: prefix (node:fs, node:http) for core modules so they are unmistakable from npm packages.
  • Keep the event loop free: move CPU-bound work to worker threads or a separate service.
  • Reach for the built-in fetch and the native test runner before adding dependencies for simple needs.
  • Never block the main thread with synchronous file or network calls in request handlers.
Last updated June 14, 2026
Was this helpful?