Node.js Introduction: What Is Node.js, How It Works And Your First Program (2026-27)
Today we are going to discuss Node.js. If you've already worked through our JavaScript Tutorial series, this is the natural next step: taking the same language you use in the browser and running it directly on a server or your local machine. In this guide we cover what Node.js actually is, the event loop and non-blocking architecture that make it fast, how it compares to traditional server-side technologies, npm and the module system, streams, real-world use cases, and a practical best-practices checklist. This tutorial finishes with an interactive code playground, a quiz, and an FAQ.
📋 Table of Contents
- What Is Node.js?
- History and Origin of Node.js
- Node.js Architecture Explained
- Understanding the Event Loop
- Blocking vs. Non-Blocking Code
- Key Features of Node.js
- Node.js vs. Traditional Server-Side Technologies
- Installing Node.js
- Your First Node.js Program
- Understanding npm
- Node.js Modules Explained
- Streams and the EventEmitter Pattern
- Real-World Use Cases
- Node.js and SAP
- Best Practices Checklist
- Quick Reference Table
- Try It Yourself: Code Playground
- Practice Quiz
- Frequently Asked Questions (FAQ)
✅ What Is Node.js?
Node.js is an open-source, cross-platform JavaScript runtime environment that allows developers to run JavaScript code outside of a web browser. Before Node.js existed, JavaScript was almost exclusively a browser language - it lived inside <script> tags, manipulated web pages, and had no real way to talk to a file system, a database, or a network socket directly. Node.js changed that completely by taking Google Chrome's V8 JavaScript engine and embedding it inside a standalone program that can run on a server, a desktop, or even a Raspberry Pi.
In simple terms, Node.js lets you use the same language - JavaScript - to build both the front end of a website (what runs in the browser) and the back end (what runs on the server). This single fact is one of the biggest reasons Node.js became so popular: a JavaScript developer no longer needs to learn a completely separate language like Java, PHP, or C# just to build server-side logic.
Node.js is built on three core pillars:
⚙️ V8 Engine
Google's high-performance JavaScript and WebAssembly engine, originally built for Chrome, which compiles JavaScript directly into machine code rather than interpreting it line by line.
🔗 libuv
A C library that gives Node.js access to the underlying operating system's file system, networking, and concurrency features, and provides the event loop itself.
📦 Core Modules
A set of built-in JavaScript modules - like http, fs, and path - that provide ready-to-use functionality without needing any external installation.
It's worth being clear about what Node.js is not, since this trips up a lot of newcomers. Node.js is not a web framework - frameworks like Express, Fastify, or NestJS are built on top of Node.js to make certain tasks, like routing HTTP requests, easier. Node.js is also not a database, and not a replacement for the browser; it simply provides the environment and low-level building blocks that these higher-level tools rely on.
✅ History and Origin of Node.js
Node.js was created by Ryan Dahl and first released in 2009. At the time, most web servers handled concurrent users using a thread-per-connection or process-per-connection model - Apache, for example, would often spin up a new thread for every incoming request. This worked, but it did not scale well when thousands of users tried to connect at once, because each thread consumes memory and CPU time even when it is simply waiting for a slow database query or file read to finish.
Dahl's key insight was that most of the time a web server spends handling a request is actually spent waiting - waiting for a database, waiting for a file, waiting for another network service to respond. Instead of creating a new thread for every connection, a single thread could juggle thousands of connections by switching to other work whenever one operation was waiting on I/O (input/output), and coming back to it only when the result was ready.
Since then, Node.js has grown enormously. It is now maintained by the OpenJS Foundation, has millions of published packages available through npm, and is used by companies including Netflix, PayPal, LinkedIn, Uber, and Walmart for parts of their production infrastructure.
✅ Node.js Architecture Explained
To really understand Node.js, it helps to compare it with a traditional multi-threaded server. In a traditional model, every request typically gets its own thread. If you have 10,000 simultaneous connections, you might end up with 10,000 threads, each consuming its own stack memory, with the operating system spending significant effort switching between them.
Node.js instead uses a single-threaded event loop for executing your JavaScript code, combined with a background thread pool (managed by libuv) for operations that are genuinely expensive, like reading large files or certain cryptographic calculations. Here is the general flow:
- A request or task arrives (for example, an HTTP request hits your server).
- Node.js registers the task and, if it involves I/O, hands off the actual waiting to the operating system or to libuv's background thread pool.
- The main thread is immediately free to handle the next incoming request - it does not sit idle waiting.
- When the I/O operation completes, its callback function is placed into a queue.
- The event loop continuously checks this queue and executes callbacks when the main thread is free.
This design means a single Node.js process can comfortably handle thousands of concurrent connections without creating thousands of threads, as long as the workload is I/O-heavy (network calls, file access, database queries) rather than CPU-heavy (complex mathematical computation, image processing, video encoding).
✅ Understanding the Event Loop
The event loop is the heart of Node.js and is often the most confusing concept for beginners. Think of it as a continuously spinning manager that asks one question over and over: "Is there anything ready to be executed right now?"
The event loop moves through several phases on every cycle (often called a "tick"):
| Phase | What Happens |
|---|---|
| Timers | Executes callbacks scheduled by setTimeout() and setInterval() whose time has elapsed. |
| Pending Callbacks | Executes I/O callbacks deferred from the previous cycle. |
| Poll | Retrieves new I/O events and executes their callbacks; most of the work happens here. |
| Check | Executes callbacks scheduled with setImmediate(). |
| Close Callbacks | Handles cleanup events, like a closed socket. |
What makes this powerful is that your JavaScript code never has to manually manage threads, locks, or synchronization. You simply write functions that say "when this operation finishes, do this," and the event loop guarantees they will run at the right time, on the same thread, one at a time, without race conditions between your own JavaScript callbacks.
✅ Blocking vs. Non-Blocking Code
The difference between blocking and non-blocking code is easiest to understand by comparing two versions of the same task: reading a file from disk. In a blocking (synchronous) approach, the program stops everything else until the read finishes:
// Blocking (synchronous) file read
const fs = require('fs');
const data = fs.readFileSync('notes.txt', 'utf8');
console.log(data);
console.log('This line waits until the file is fully read.');
Now compare it with the non-blocking (asynchronous) version, which is the style Node.js encourages:
// Non-blocking (asynchronous) file read
const fs = require('fs');
fs.readFile('notes.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log(data);
});
console.log('This line runs immediately, without waiting for the file.');
In the second example, the final console.log statement will almost always print before the file contents, because Node.js does not pause execution while the disk read happens in the background. Once the file read finishes, the callback function you supplied is placed into the event loop's queue and executed as soon as the main thread is free.
Modern Node.js code often expresses this same idea using Promises and async/await, which reads almost like synchronous code while still being fully non-blocking underneath:
const fs = require('fs/promises');
async function readNotes() {
const data = await fs.readFile('notes.txt', 'utf8');
console.log(data);
}
readNotes();
console.log('Still runs first, just like before.');
async/await does not change how Node.js actually executes code behind the scenes; it simply gives developers a cleaner, more readable way to write asynchronous logic without deeply nested callback functions - a pattern earlier Node.js code was often criticized for and that developers nicknamed "callback hell."
✅ Key Features of Node.js
✅ Node.js vs. Traditional Server-Side Technologies
A common question for beginners is how Node.js compares to server-side technologies like PHP, Java, or Python-based frameworks. The honest answer is that there is no universally "best" choice - each is suited to different situations.
| Aspect | Node.js |
|---|---|
| Concurrency model | Single-threaded event loop with async I/O, versus one thread/process per request in many traditional servers. |
| Best suited for | I/O-heavy apps - APIs, real-time apps, streaming - more than raw CPU-heavy computation. |
| Language | JavaScript on both client and server, rather than a separate back-end language. |
| Learning curve for JS devs | Low - same language, new environment. |
| Package ecosystem | npm - very large and JavaScript-focused. |
In practice, Node.js tends to shine in scenarios involving many simultaneous, relatively lightweight connections - chat applications, live dashboards, streaming services, and REST or GraphQL APIs - while CPU-intensive tasks such as heavy image processing are often better handled by offloading to worker threads or a separate service, even within a Node.js-based system.
✅ Installing Node.js
Getting Node.js running on your machine takes only a few minutes:
- Visit the official Node.js website at nodejs.org.
- Download the LTS (Long Term Support) version - the recommended, most stable release for most users, rather than the "Current" version, which includes newer but less battle-tested features.
- Run the installer for your operating system and accept the default settings, which also install npm automatically.
- Open a terminal and verify the installation.
// Check the installed Node.js version
node -v
// Check the installed npm version
npm -v
If both commands print version numbers instead of an error, Node.js is successfully installed and you are ready to run your first program.
nvm (Node Version Manager) rather than reinstalling Node.js repeatedly.
✅ Your First Node.js Program
Let's write the traditional "Hello World" program, then go one step further and build a tiny web server - the single most common thing beginners want to see Node.js do.
Hello World
Create a new file called app.js and add the following line:
console.log("Hello, Node.js!");
Now run it from your terminal, in the same folder as the file:
node app.js
You should see Hello, Node.js! printed directly in your terminal. Notice there is no browser involved at all - this is JavaScript running directly on your machine.
A Minimal Web Server
Node.js includes a built-in http module that lets you create a fully functional web server in just a handful of lines, with no external framework required:
const http = require('http');
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello from my first Node.js server!');
});
server.listen(3000, () => {
console.log('Server running at http://localhost:3000/');
});
Save this as server.js and run node server.js. Open a browser and navigate to http://localhost:3000/, and you will see the text response rendered on the page. This tiny script demonstrates the essence of Node.js: it imported a core module, defined a callback to handle each incoming request, and started listening for connections - all without installing any third-party library.
✅ Understanding npm
npm, short for Node Package Manager, is installed automatically alongside Node.js and serves two purposes: it is a command-line tool for installing and managing packages, and it is also the name of the world's largest software registry, hosting well over a million open-source JavaScript packages.
Initializing a Project
npm init -y
This command creates a package.json file, which acts as the manifest for your project - it records the project name, version, dependencies, and scripts.
Installing a Package
npm install express
This downloads the popular Express.js framework into a local node_modules folder and records it as a dependency inside package.json, so anyone else who clones your project can run npm install and get the exact same dependencies.
Running Scripts
Developers commonly define custom commands inside the scripts section of package.json, such as npm start or npm test, which makes it easy for anyone on a team to run the project the same way regardless of the underlying commands involved.
✅ Node.js Modules Explained
Node.js organizes code into modules, which keeps applications maintainable as they grow. There are three categories of modules you will encounter:
🧱 Core Modules
Built directly into Node.js and always available without installation - such as fs (file system), http (web servers and clients), path (file path utilities), and events (custom event emitters).
📁 Local Modules
Files you create yourself within your project and import using a relative path, like ./math.js.
🌍 Third-Party Modules
Packages installed via npm and published by the community, such as Express, Axios, or Mongoose.
Here is a simple example of creating and using a local module:
// math.js — a local module
function add(a, b) {
return a + b;
}
module.exports = { add };
// app.js — using the local module
const math = require('./math');
console.log(math.add(5, 7)); // 12
Newer versions of Node.js also support ECMAScript Modules (ESM) using import and export syntax - the same module syntax used in modern front-end JavaScript, further reinforcing Node.js's "one language everywhere" philosophy.
✅ Streams and the EventEmitter Pattern
Two concepts appear throughout almost every serious Node.js application: streams and the EventEmitter pattern. Understanding them early makes the rest of the ecosystem, including popular frameworks, much easier to follow.
Streams
A stream is a way of handling data piece by piece rather than loading it all into memory at once. Imagine reading a two-gigabyte video file - loading the entire file into memory before processing it would be slow and could easily exhaust available memory. Instead, Node.js streams let you read (or write) data in small chunks, processing each chunk as it arrives.
const fs = require('fs');
const readStream = fs.createReadStream('largefile.txt', 'utf8');
readStream.on('data', (chunk) => {
console.log(`Received ${chunk.length} characters.`);
});
readStream.on('end', () => {
console.log('Finished reading the file.');
});
There are four main types of streams in Node.js: Readable (a source of data, like reading a file), Writable (a destination for data, like writing to a file), Duplex (both readable and writable, like a network socket), and Transform (a duplex stream that modifies data as it passes through, such as a compression utility). Streams are memory-efficient and underpin the built-in HTTP module's handling of request and response bodies.
The EventEmitter Pattern
Node.js is fundamentally event-driven, and the events core module exposes the EventEmitter class that powers this behavior throughout the platform - streams, HTTP servers, and countless third-party libraries all extend or use EventEmitter internally.
const EventEmitter = require('events');
const orderEvents = new EventEmitter();
orderEvents.on('orderPlaced', (order) => {
console.log(`New order received: ${order.id}`);
});
orderEvents.emit('orderPlaced', { id: 'ORD-1001' });
This pattern - defining named events, listening for them with .on(), and triggering them with .emit() - is one of the clearest illustrations of why Node.js feels natural to JavaScript developers coming from the browser, where listening for events like clicks and key presses is already second nature.
✅ Real-World Use Cases
🔌 REST and GraphQL APIs
Node.js is an extremely common choice for building the backend API layer that mobile apps and single-page web apps consume.
💬 Real-Time Applications
Chat applications, live notifications, and collaborative tools rely heavily on Node.js paired with WebSockets for instant, bidirectional communication.
🎬 Streaming Services
Node.js's stream-based architecture makes it well suited to handling continuous data, such as video or audio streaming.
🧩 Microservices
Many companies break large applications into small, independently deployable Node.js services that communicate over lightweight protocols.
🛠️ Command-Line Tools
Many popular developer tools, including build tools and scaffolding generators, are themselves written in Node.js.
✅ Node.js and SAP
Node.js is also commonly used to build middleware layers that connect SAP systems to external web applications, mobile apps, or third-party services - for example, exposing SAP OData services through a lightweight Node.js API layer. For an SAP consultant learning to code, Node.js is often a practical entry point into full-stack development because SAP's own cloud tooling documentation and SAP Business Application Studio both support it natively.
✅ Node.js Best Practices Checklist
- Use synchronous methods only for one-time startup tasks - like reading a config file at boot - never inside repeated request handlers.
- Handle errors in every callback and Promise - check the error argument in callbacks, and use
.catch()ortry/catchwithawait. - Never block the event loop - move CPU-heavy work into
worker_threadsor a separate service rather than running it synchronously on the main thread. - Keep dependencies lean and audited - run
npm auditperiodically and remove unused packages. - Use environment variables for configuration - never hard-code passwords or API keys directly into source files.
- Install the LTS version for production projects rather than the bleeding-edge Current release.
- Log and monitor your process so crashes and unhandled rejections are caught quickly in production.
// Reading an environment variable safely
const port = process.env.PORT || 3000;
console.log(`Server will run on port ${port}`);
✅ Node.js - Quick Reference Table
| Term | One-Line Meaning |
|---|---|
| Node.js | A JavaScript runtime environment for running JS outside the browser. |
| V8 | Google's engine that compiles JavaScript into machine code. |
| libuv | C library providing the event loop and async I/O access. |
| Event Loop | The mechanism that executes callbacks when the main thread is free. |
| npm | Node Package Manager - installs and manages JavaScript packages. |
| package.json | The manifest file describing a project's dependencies and scripts. |
| Core Module | Built-in Node.js functionality, like fs or http. |
| Stream | A way of processing data piece by piece instead of all at once. |
| EventEmitter | The core class powering Node.js's event-driven design. |
✅ Try It Yourself: Code Game Playground
Edit the JavaScript below and click "Run" to see simulated console output. This demonstrates basic JavaScript concepts you would run identically inside Node.js using node yourfile.js.
✅ Practice - Yes / No Quiz
1. Is Node.js a programming language?
2. Does Node.js run JavaScript using the V8 engine?
3. Should CPU-intensive computation run directly on Node.js's main thread as best practice?
4. Is npm installed automatically alongside Node.js?
5. Does the asynchronous version of fs.readFile block later lines of code from running?