What Is Node.js? V8, libuv, Event-Driven I/O, Modules, npm, and Modern Node 26 Explained
Node.js is a JavaScript runtime built on the V8 JavaScript engine. It lets JavaScript run outside the browser and adds server/runtime APIs for networking, files, processes, streams, cryptography, workers, testing, and more.
Node is commonly used for APIs, realtime servers, CLI tools, background workers, build tooling, and integration services.
As of September 2026, Node.js 26.8.2 is the Current release and Node.js 24 is the active LTS line.
Node.js is a runtime, not a framework
Node itself provides the runtime and standard APIs.
Frameworks such as Express, Fastify, NestJS, or full-stack web frameworks run on top of Node.
You can build an HTTP server with only Node's standard library:
import http from 'node:http';
const server = http.createServer((req, res) => {
res.writeHead(200, { 'content-type': 'text/plain' });
res.end('Hello from Node');
});
server.listen(3000);
A framework mainly adds higher-level routing, validation, middleware, dependency patterns, or application conventions.
V8 executes JavaScript
V8 is the JavaScript engine used by Node.
It parses and executes JavaScript, manages JavaScript objects/heap, and includes JIT optimization and garbage collection.
Node combines V8 with native runtime components that add capabilities a browser page normally does not have, such as direct filesystem and server socket APIs.
libuv provides much of the asynchronous runtime machinery
Node uses libuv, a native C library, for its event loop and many cross-platform asynchronous behaviors.
libuv helps coordinate:
- network I/O
- timers
- selected filesystem/DNS work
- an internal worker pool for certain operations
This is a key reason Node can keep one JavaScript thread productive while many I/O operations are waiting elsewhere.
Event-driven does not mean everything runs in parallel
Ordinary JavaScript callbacks in a Node process execute on a main event-loop thread.
If one callback performs a long CPU-heavy loop, other callbacks wait.
Node's scalability comes mainly from not blocking the JavaScript thread while asynchronous I/O waits.
For CPU-heavy JavaScript, use worker threads, processes, or another worker/service rather than monopolizing the main event loop.
async and await make I/O code readable
A typical asynchronous function can look synchronous:
async function loadOrder(id) {
const order = await repository.find(id);
return order;
}
While repository.find() is waiting asynchronously, Node can process other callbacks.
When the Promise resolves, continuation of the function runs later through Promise/microtask scheduling.
async does not make a synchronous CPU function non-blocking.
Node includes a large standard API surface
Modern Node includes modules/APIs for areas such as:
- HTTP/HTTPS/HTTP2
- TCP/UDP
- filesystem/path
- crypto/Web Crypto
- streams
- compression
- child processes
- worker threads
- performance/diagnostics
- testing
- SQLite
- Web Streams
The runtime has become substantially more capable without third-party packages than older Node tutorials suggest.
Node supports ES Modules and CommonJS
Node has two module systems.
ES Modules
import { readFile } from 'node:fs/promises';
export function load() {}
ES Modules are the JavaScript standard module format.
CommonJS
const fs = require('node:fs');
module.exports = something;
CommonJS is Node's older module system and remains widely used.
Modern projects should choose their module model deliberately and avoid mixing conventions accidentally.
package.json describes a Node package/application
A typical package.json can define:
- package name/version
- scripts
- dependencies
- module type
- engines
- exports
For ES Module .js files, projects often use:
{
"type": "module"
}
Node uses package/module resolution rules to determine how files and dependencies are loaded.
npm manages packages
npm is the default package manager distributed with standard Node installations.
Install a dependency with:
npm install fastify
The package is recorded in the project manifest/lockfile depending on your workflow.
Commit your lockfile for normal applications so CI and developers resolve consistent dependency versions.
Dependencies run with your application's trust
A package can execute application-side code and, depending on install configuration, installation scripts.
Treat dependencies as supply-chain code:
- minimize unnecessary packages
- review important dependencies
- scan vulnerabilities
- keep versions supported
- inspect suspicious lifecycle scripts
The convenience of npm should not turn dependency selection into a zero-review action.
Modern Node can run some TypeScript directly
Current Node includes stable built-in TypeScript type stripping for erasable TypeScript syntax.
That means selected .ts code can execute without a separate transpile step.
Important limitation: Node's built-in support does not perform TypeScript type checking and intentionally does not implement every tsconfig-dependent transform.
For full TypeScript compilation/type checking, use TypeScript/tooling as normal.
Node has built-in fetch
Modern Node provides a browser-compatible global fetch() implementation powered by Undici.
const response = await fetch('https://example.com/api');
For production calls, still design:
- cancellation/timeouts
- connection behavior
- retries
- idempotency
A built-in HTTP API does not make remote dependencies reliable automatically.
Streams process data incrementally
Node's HTTP requests/responses and many filesystem operations integrate with streams.
Instead of loading a huge file completely into memory:
read entire object -> transform -> send
you can process chunks:
read -> transform -> write
Streams are especially important for file transfer and high-throughput services because they support backpressure and lower peak memory.
Events are a core programming pattern
Node's EventEmitter lets objects publish named events:
emitter.on('ready', () => {
console.log('ready');
});
Ordinary EventEmitter listeners are invoked synchronously when the event is emitted.
Do not assume “event” automatically means “runs later.”
Worker threads run JavaScript in parallel
node:worker_threads provides actual parallel JavaScript execution in separate threads.
Workers are most useful for CPU-intensive tasks.
Official docs explicitly note that they generally do not improve I/O-heavy work, because Node's asynchronous I/O already handles that efficiently.
Use worker pools for repeated tasks rather than spawning a new worker for every request.
Child processes provide stronger process isolation
Node can launch other processes with child_process.
Separate processes have separate V8 heaps and can isolate crashes/resources more strongly than worker threads.
Use them for:
- external commands
- independent process workloads
- multi-process application patterns
Be careful with untrusted shell input; command construction can create injection vulnerabilities.
Node is garbage collected
Developers do not manually free normal JavaScript objects.
V8 reclaims objects that are no longer reachable.
You can still leak memory by keeping references alive through:
- global caches
- listeners
- queues
- closures
GC provides memory management, not automatic application lifecycle correctness.
Node and the browser share JavaScript, not all APIs
Both environments support JavaScript/ECMAScript features, but runtime APIs differ.
Browser code has things such as DOM APIs.
Node has things such as:
node:fs- process APIs
- server sockets
Many web-standard APIs have become shared across both environments (fetch, Web Streams, Web Crypto), but do not assume every browser API exists in Node or vice versa.
What Node is good at
Node is a strong fit for:
- JSON APIs
- BFF services
- realtime WebSocket servers
- integration-heavy SaaS
- streaming responses
- CLI/build tooling
- serverless functions
These workloads often spend much of their time waiting on network/storage rather than executing CPU-heavy JavaScript.
When Node may not be the best fit
Another runtime may be simpler for:
- CPU-intensive algorithms that dominate request time
- low-level systems software
- highly constrained native resource environments
- workloads whose key libraries live in another ecosystem
Node can still offload CPU work, but if nearly every request requires that workaround, reconsider the runtime boundary.
Current Node release model
Node publishes Current and LTS release lines.
Production teams generally prefer an active LTS line unless they specifically need Current features and accept the faster lifecycle.
Unsupported EOL releases stop receiving security fixes and gradually lose ecosystem support.
Keep runtime versions current and test major upgrades deliberately.
Beginner checklist
Remember:
- Node.js is a runtime, not a framework.
- V8 executes JavaScript.
- libuv provides the event loop and asynchronous runtime plumbing.
- I/O can wait without blocking JavaScript callbacks.
- CPU-heavy synchronous work blocks the main event loop.
- npm manages third-party packages.
- Node supports ES Modules and CommonJS.
- Modern Node includes built-in fetch, testing, TypeScript stripping, SQLite, streams, and more.
- Worker threads are for parallel CPU-heavy JavaScript.
- Garbage collection does not prevent logical memory leaks.
Node.js is best understood as JavaScript plus a server/runtime platform: V8 executes the language, libuv coordinates asynchronous work, and Node's standard library exposes operating-system and network capabilities needed to build production software.
Official references
BUILDING AN AI PRODUCT?
Talk to Lofingo about custom autonomous AI agents, enterprise workflow automation, and scalable software systems.
EXPLORE AI SERVICES →