NodeJSWhat is Node.js?

What is Node.js?

Node.js is an open-source, cross-platform runtime environment that executes JavaScript outside the browser — on servers, on your laptop, inside containers, on edge networks, anywhere there is a CPU. It was created in 2009 by Ryan Dahl, who combined three things: Google Chrome's V8 JavaScript engine, a C library called libuv for asynchronous I/O, and a set of C++ bindings and JavaScript APIs that expose the operating system to your code.

Before Node.js, JavaScript was confined to the browser. To write the server side of a web application you reached for PHP, Ruby, Python, Java, or C#. Node.js broke that wall: it took the language millions of developers already knew and gave it the system-level capabilities a server needs — a file system, networking (TCP/UDP/HTTP), processes, streams, timers, and cryptography. The result is one language across the entire stack: the same engineer can write the React front end and the API that feeds it, sharing types, validation logic, and even whole modules between them.

Runtime vs language vs framework

This distinction trips up almost every beginner, and getting it straight now will save you confusion later. These are three different layers:

Term

What it is

Examples

Language

The syntax and rules, standardized as ECMAScript

JavaScript, TypeScript (compiles to JS)

Runtime

A program that executes the language and exposes platform APIs

Node.js, Deno, Bun, the browser

Framework / library

Code you install on top of a runtime to build faster

Express, NestJS, Fastify, Next.js

The mental model
If JavaScript is a language you can *speak*, then the **browser** and **Node.js** are two different *places* where that language is understood. The browser hands JavaScript a `document`, a `window`, and the DOM. Node.js hands it a `process`, a `require`/`import` system, `Buffer`, and the file system instead. The grammar is identical; the vocabulary of available APIs differs. See [Node.js vs Browser JavaScript](/nodejs/nodejs-vs-browser).
What is "a runtime," concretely?

A JavaScript engine like V8 only knows the pure language — variables, functions, objects, Math, JSON, promises. It has no idea how to read a file or open a network socket, because those are not part of JavaScript; they are operating-system features. A runtime is the engine plus the bridge to the outside world.

Node.js is that bridge. Architecturally it stacks like this:

The Node.js stack

Text
┌─────────────────────────────────────────────┐
│   Your application code (JavaScript)          │
├─────────────────────────────────────────────┤
│   Node.js core API  (fs, http, crypto, ...)   │  ← JavaScript
├─────────────────────────────────────────────┤
│   Node.js bindings  (C++ glue layer)          │  ← C++
├──────────────────────┬──────────────────────┤
│   V8 engine          │   libuv               │  ← C++ / C
│   (runs your JS,     │   (event loop,        │
│    manages memory)   │    async I/O,         │
│                      │    thread pool)       │
├──────────────────────┴──────────────────────┤
│   Operating system (files, sockets, timers)   │
└─────────────────────────────────────────────┘

We explore each layer separately: V8 runs your JavaScript and manages memory; libuv provides the event loop and asynchronous I/O; and the whole assembly is the subject of Node.js Architecture Overview.

Your first taste

Create a file called app.js and run it with node app.js:

app.js

JS
// This runs on the server / your machine — no browser involved.
console.log('Hello from Node.js!')
console.log('Running on Node', process.version)
console.log('Platform:', process.platform, process.arch)

// 'process' does not exist in a browser — it is pure Node.
console.log('This file:', __filename)
Hello from Node.js!
Running on Node v20.11.0
Platform: darwin arm64
This file: /Users/you/app.js

Both process and __filename exist in Node.js but not in a browser. Those two names alone hint at everything Node.js adds: introspection of the running process and access to the file system.

The defining idea: an event-driven, non-blocking runtime

Node.js was built around two tightly linked ideas that, together, make it exceptional for I/O-heavy network applications:

  • A single-threaded event loop. Instead of dedicating one operating-system thread to each incoming request (the traditional "thread-per-request" model used by classic Apache/PHP and Java servlets), Node.js runs your JavaScript on one thread and uses a loop to react to events as they occur.

  • Non-blocking, asynchronous I/O. When Node.js reads a file, queries a database, or calls another API, it does not sit and wait for the answer. It hands the slow work off to the operating system (or to a background thread pool), registers a callback, and immediately moves on to serve other work. When the result is ready, the callback is queued and the event loop runs it.

The pay-off: one Node process can juggle tens of thousands of simultaneous connections, because it spends its time doing useful work rather than blocked, waiting on slow operations. We unpack the mechanics in How Node.js Works and Single-Threaded, Non-Blocking I/O.

Blocking vs non-blocking — see the difference

This contrast is the single most important thing to internalize about Node.js. Both snippets read a file; only one keeps the program responsive.

Two ways to read a file

JS
import fs from 'node:fs'

// BLOCKING: the whole process freezes here until the file is fully read.
// On a server, EVERY other user waits during this pause.
const dataSync = fs.readFileSync('big.log', 'utf8')
console.log('A: read finished (blocking)')

// NON-BLOCKING: the read happens in the background. Node keeps running.
fs.readFile('big.log', 'utf8', (err, data) => {
  if (err) throw err
  console.log('C: read finished (non-blocking)')
})
console.log('B: this prints before C, while the file is still loading')
A: read finished (blocking)
B: this prints before C, while the file is still loading
C: read finished (non-blocking)
The cardinal rule of Node.js
Because one thread serves *all* requests, a single slow **synchronous** operation freezes *everyone*. The golden rule — repeated throughout this tutorial — is **never block the event loop**. Prefer the asynchronous version of every API, and push heavy CPU work to [Worker Threads](/nodejs/worker-threads) or a separate process.
A tiny server — where Node becomes a backend

The browser can never open a listening socket. Node.js can, using the built-in http module and no framework at all:

server.js

JS
import http from 'node:http'

const server = http.createServer((req, res) => {
  // This callback runs once PER request, on the single main thread.
  res.writeHead(200, { 'Content-Type': 'application/json' })
  res.end(JSON.stringify({ message: 'Hello from a Node.js server' }))
})

server.listen(3000, () => console.log('Listening on http://localhost:3000'))

Run it with node server.js and visit the URL. One process, one thread, yet it can serve many clients concurrently because each request handler is short and any slow work inside it would be asynchronous.

Node.js, Deno, and Bun

Node.js is no longer the only server-side JavaScript runtime. Knowing the landscape helps you understand Node's design choices and where it is heading.

Runtime

Engine

Distinctive traits

Node.js (2009)

V8

The mature standard; huge ecosystem; CommonJS + ESM; the de-facto choice

Deno (2018)

V8

Built by Node's creator; secure-by-default permissions; TypeScript built in; web-standard APIs

Bun (2022)

JavaScriptCore

Speed-focused; bundler + test runner + package manager built in; mostly Node-compatible

The competition has pushed Node.js forward: it now ships a native test runner, a built-in fetch, watch mode, and .env file loading — features inspired by its rivals.

The npm ecosystem

A runtime is only as useful as the code available for it. Node's package manager, npm, hosts the largest software registry in the world — over two million packages. Need to validate input, sign a JWT, talk to PostgreSQL, parse a CSV, or send email? A well-tested package almost certainly exists. This ecosystem is arguably Node's single biggest advantage — and, because a typical app pulls in hundreds of transitive dependencies, also its biggest source of security and supply-chain risk. We cover the package manager in depth starting at Introduction to npm.

Where Node.js shines (and where it does not)
  • APIs & web servers — REST and GraphQL backends that mostly shuttle data between clients and databases. Ideal: almost pure I/O.

  • Real-time apps — chat, collaboration, multiplayer games, live dashboards over WebSockets, where many connections sit mostly idle.

  • Microservices & serverless — small, fast-starting, low-memory services and functions.

  • Tooling & CLIs — Webpack, Vite, ESLint, Prettier, and most of the front-end toolchain run on Node.js.

  • Streaming — processing large files or data feeds chunk-by-chunk without loading everything into memory.

Node.js is a poor fit for heavy CPU-bound number crunching — video encoding, image processing at scale, scientific computing, machine-learning training — because a long computation blocks the single thread. See When to Use (and Not Use) Node.js for the honest trade-offs and the ways to work around them.

Recap — the one-paragraph version
Node.js is a runtime that runs JavaScript outside the browser by pairing the **V8** engine with **libuv**. It is **single-threaded** for your code but achieves massive **concurrency** through a **non-blocking, event-driven** model: slow I/O is delegated and handled via callbacks fired by an **event loop**. This makes it superb for I/O-bound, high-connection workloads and weak at CPU-bound ones. Its **npm** ecosystem is the largest in software.
What's next
Read the [History of Node.js](/nodejs/history-of-nodejs) to understand *why* it was created, then [Why Use Node.js?](/nodejs/why-nodejs) for the practical case, and [How Node.js Works](/nodejs/how-nodejs-works) for the mechanics under the hood.