Node.js is a JavaScript runtime that lets us run JS outside the browser. Before Node, JavaScript only ran inside browsers. Ryan Dahl built Node in 2009 so we could use the same language on the server too.
In simple language — Node.js takes the V8 engine out of Chrome, glues it to a C library called libuv, and gives us file system access, networking, child processes, and everything else a server needs.
The two main pieces
Node is essentially two things stitched together:
- V8 — Google’s JavaScript engine (the same one Chrome uses). It compiles JS to native machine code. This is what actually runs our code.
- libuv — a C library that handles non-blocking I/O. It gives us the event loop, the thread pool, and async file/network operations.
Everything else (the fs, http, crypto modules, etc.) is a thin layer on top of these two.
Non-blocking I/O is the big idea
Most server work is waiting — for a database, for a file, for an HTTP response. Traditional servers spin up a thread per request. Node uses one thread and an event loop. When we call fs.readFile(), Node hands the work off to libuv, returns immediately, and runs other code. When the file is ready, our callback runs.
The only thread that runs our JavaScript is single. But the I/O happens in parallel under the hood. That’s why Node feels fast for I/O-heavy workloads.
const fs = require("node:fs");
console.log("1. starting");
fs.readFile("./data.json", "utf8", (err, data) => {
console.log("3. file read done");
});
console.log("2. continuing without waiting");
// Output order: 1, 2, 3
What we actually use Node for
- HTTP / REST APIs — Express, Fastify, NestJS
- Real-time apps — WebSockets, chat, multiplayer games (great fit because of the event loop)
- CLI tools — npm itself, ESLint, Prettier, Vite are all Node CLIs
- Build tooling — bundlers, transpilers, test runners
- Microservices — small, fast HTTP services
- Streaming pipelines — log processing, file transforms
Where Node is NOT a great fit
Node has one main thread for JS. CPU-heavy work (video encoding, ML inference, big math loops) blocks that thread and stalls everything. For CPU-bound work, we’d reach for Go, Rust, or Python with native libs — or offload to worker threads within Node.
Quick history
- 2009 — Ryan Dahl releases Node
- 2010 — npm launches
- 2015 — io.js fork merges back, Node 4.0 released, Node Foundation forms
- 2019 — Node Foundation + JS Foundation merge into OpenJS Foundation
- 2020+ — ES Modules become stable,
node:protocol, built-infetch,--testrunner
Today Node powers Netflix, LinkedIn, PayPal, Uber’s API gateway, and basically every company’s tooling layer.