Node.js is still the most widely deployed JavaScript runtime for server-side code in 2026, despite competition from Deno and Bun. Node.js 22 LTS (released April 2025) stabilised the built-in test runner, shipped native fetch, and added experimental TypeScript stripping — reducing the tooling overhead that made Node.js feel heavyweight compared to its newer competitors. The fundamentals, though, have not changed: the event loop, async I/O, and streams are still what you need to understand to be productive.
What changed in 2026
- Node.js 22 LTS. Active support through 2027. Key additions: built-in
node:test and node:assert, fetch stable, WebStreams stable, --env-file for .env loading without dotenv, --watch mode without nodemon.
- TypeScript stripping (experimental).
node --experimental-strip-types server.ts runs TypeScript files directly. No ts-node, no tsx wrapper required — though you still need tsc for type checking.
- ESM is the default expectation. Package authors have fully migrated. New
create-* templates default to ESM. CJS interop still exists but is no longer the path for new code.
- Deno/Bun pressure drove improvements. Startup time, built-in tooling, and security defaults all improved faster in the last two years than in the previous five.
The event loop — understand this first
Node.js is single-threaded. All I/O is non-blocking. The event loop processes callbacks in a defined order:
timers → pending callbacks → idle/prepare → poll → check → close callbacks
console.log("1");
setTimeout(() => console.log("2"), 0); // macro-task (timers phase)
Promise.resolve().then(() => console.log("3")); // micro-task
queueMicrotask(() => console.log("4")); // micro-task
console.log("5");
// Output: 1, 5, 3, 4, 2
Micro-tasks (Promises, queueMicrotask) run before the next event-loop phase. If you drain the microtask queue with an infinite loop, the event loop stalls — this is how you freeze a Node server.
Setup for 2026
# Install Node.js 22 LTS via nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
nvm install 22 && nvm use 22
# New ESM project
mkdir my-app && cd my-app
npm init -y
# Set "type": "module" in package.json
package.json with ESM:
{
"type": "module",
"engines": { "node": ">=22" }
}
HTTP without a framework
import { createServer } from "node:http";
const server = createServer((req, res) => {
if (req.method === "GET" && req.url === "/health") {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ ok: true }));
return;
}
res.writeHead(404);
res.end("Not found");
});
server.listen(3000, () => console.log("Listening on :3000"));
Build this once. When you reach for Express or Hono next, you will understand what they add.
Built-in test runner (Node 22)
// sum.test.js
import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { sum } from "./sum.js";
describe("sum", () => {
it("adds two numbers", () => {
assert.equal(sum(1, 2), 3);
});
});
node --test # runs all *.test.js files
node --test --watch # re-runs on change
No Jest, no Vitest required for basic testing.
Framework comparison
| Framework |
Focus |
Bundle size |
When to use |
| Express |
Minimal, flexible |
~200 KB |
Existing projects, simple APIs |
| Hono |
Edge-native, fast |
~14 KB |
New APIs, edge runtimes |
| Fastify |
High performance |
~150 KB |
High-throughput APIs |
| NestJS |
Enterprise, full-stack |
Large |
Large teams, DI containers |
Hono is the 2026 recommendation for new projects — it runs on Node.js, Deno, Bun, and Cloudflare Workers without code changes.
How to pick what to learn
| Your goal |
Focus |
| REST API |
Hono or Express + async/await patterns |
| Real-time |
WebSockets with ws or Socket.IO |
| File processing |
Node.js Streams, pipeline() |
| Background jobs |
BullMQ + Redis |
| Database access |
Drizzle ORM or Prisma |
Common mistakes
Unhandled promise rejections. In Node.js 22, an unhandled rejection crashes the process. Always attach .catch() or use try/catch in async functions.
Blocking the event loop. CPU-bound work (heavy JSON parsing, image processing) blocks all requests. Move it to a Worker Thread or a separate process.
Using process.exit() without cleanup. Listen for SIGTERM and SIGINT, close DB connections and HTTP server gracefully before exiting.
Not validating request bodies. Libraries like zod should validate incoming request data before it touches your business logic.
What to skip
nodemon for development. Node.js 22's --watch flag handles restarts natively — no extra dependency.
dotenv for .env loading. node --env-file=.env server.js loads it without any package.
node-fetch polyfill. fetch has been stable in Node.js since v21. Remove the polyfill.
FAQ
Node.js vs Deno vs Bun in 2026?
Node.js has the largest ecosystem and the most production deployments. Bun is 2–4× faster for many workloads and is worth considering for new greenfield projects. Deno 2 is strong for security-first environments. See Deno vs Node in 2026 for a full comparison.
Should I use TypeScript with Node.js?
Yes. Use TypeScript for type safety. In 2026, tsx or ts-node is common for development; experimental --experimental-strip-types removes the compilation step entirely.
What is the right way to structure a Node.js project?
Feature-based folders (/users, /posts, /auth) over layer-based (/controllers, /models, /services). Each feature folder owns its route, handler, and data access.
Is Node.js good for CPU-intensive workloads?
Not for on-the-request-path. Use Worker Threads for CPU work, or delegate to a dedicated process (Python, Go, Rust) via a job queue.
Where to go next
Build on your Node.js foundation with how to build a REST API in 2026, explore the framework ecosystem in how to learn Express in 2026, and understand async patterns deeper with the event loop explained in 2026.