|

Node.js Basics 2026: Complete Beginner Guide to Server-Side JavaScript

Node.js transformed JavaScript from a browser-only language into a full-stack powerhouse. With Node.js, you write server-side applications, CLI tools, APIs, and automation scripts using the same language you already know from the frontend. In 2026, Node.js powers some of the most demanding applications at Netflix, PayPal, Uber, and NASA. This guide teaches you the Node.js basics you need to start building server-side JavaScript applications.

This lesson assumes you are comfortable with JavaScript fundamentals including async/await, ES modules, and npm. If not, read those guides first.

What Is Node.js?

Node.js is a JavaScript runtime built on Chrome’s V8 engine. It lets you run JavaScript outside the browser — on servers, your local machine, or anywhere you need a fast scripting environment. Node.js was created by Ryan Dahl in 2009 and is now maintained by the OpenJS Foundation.

Node.js is not a framework and not a programming language. It is a runtime — a program that executes JavaScript code. Think of it like this: Chrome runs JavaScript in the browser, and Node.js runs JavaScript everywhere else. The V8 engine compiles JavaScript to native machine code, making Node.js remarkably fast for a dynamically typed language.

What makes Node.js special is its non-blocking, event-driven architecture. Instead of creating a new thread for every request (like traditional servers), Node.js handles thousands of concurrent connections on a single thread using asynchronous I/O. This makes it ideal for I/O-heavy workloads like API servers, real-time applications, and microservices.

Installing Node.js

The recommended way to install Node.js in 2026 is using a version manager. This lets you switch between Node versions for different projects.

Using nvm (Mac/Linux)

# Install nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash

# Install the latest LTS version
nvm install --lts

# Verify installation
node --version   # v22.x.x (or latest LTS)
npm --version    # 10.x.x

Using fnm (Cross-platform, faster)

# Install fnm
curl -fsSL https://fnm.vercel.app/install | bash

# Install Node
fnm install --lts
fnm use --lts

node --version

Every Node.js installation includes npm (Node Package Manager) for installing third-party packages. You can also use alternatives like pnpm or yarn.

The Event Loop

The event loop is the heart of Node.js. Understanding it is essential for writing performant server-side code. If you have read our event loop guide, you know the browser version. Node.js extends this with additional phases.

The Node.js event loop processes work in phases:

   ┌───────────────────────────┐
┌─>│        timers              │  setTimeout, setInterval
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │     pending callbacks      │  I/O callbacks deferred
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │       idle, prepare        │  Internal use
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │          poll              │  Incoming connections, data
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │          check             │  setImmediate
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │     close callbacks        │  socket.on('close')
│  └─────────────┬─────────────┘
└─────────────────┘

The critical insight: never block the event loop. Any synchronous CPU-heavy operation (parsing a huge JSON, complex calculations, image processing) blocks all other connections. Use worker threads or offload heavy work to separate processes.

// BAD: blocks the event loop
app.get('/report', (req, res) => {
  const data = generateHugeReport(); // Blocks for 5 seconds
  res.json(data); // All other requests wait
});

// GOOD: offload to a worker
import { Worker } from 'node:worker_threads';

app.get('/report', (req, res) => {
  const worker = new Worker('./report-worker.js');
  worker.on('message', (data) => res.json(data));
});

Built-in Modules

Node.js ships with powerful built-in modules. In 2026, the recommended import prefix is node: to distinguish built-in modules from npm packages.

import fs from 'node:fs/promises';    // File system
import path from 'node:path';          // Path utilities
import http from 'node:http';          // HTTP server/client
import crypto from 'node:crypto';      // Cryptography
import os from 'node:os';              // Operating system info
import url from 'node:url';            // URL parsing
import events from 'node:events';      // Event emitter
import stream from 'node:stream';      // Streams
import { Worker } from 'node:worker_threads'; // Threading

Key modules you will use constantly: fs for files, path for cross-platform paths, http for servers, and crypto for hashing and encryption. We cover the file system in detail in the next lesson.

Your First HTTP Server

Building an HTTP server from scratch teaches you what frameworks like Express abstract away.

import http from 'node:http';

const server = http.createServer((req, res) => {
  // req = incoming request, res = outgoing response
  const { method, url } = req;

  if (method === 'GET' && url === '/') {
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.end('<h1>Hello from Node.js!</h1>');
  }
  else if (method === 'GET' && url === '/api/time') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ time: new Date().toISOString() }));
  }
  else if (method === 'POST' && url === '/api/echo') {
    let body = '';
    req.on('data', chunk => body += chunk);
    req.on('end', () => {
      res.writeHead(200, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ echoed: body }));
    });
  }
  else {
    res.writeHead(404, { 'Content-Type': 'text/plain' });
    res.end('Not Found');
  }
});

const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
  console.log(`Server running at http://localhost:${PORT}`);
});

Run this with node server.js and visit http://localhost:3000. You have a working web server in about 30 lines. This is the foundation that every Node.js web framework builds upon.

Reading & Writing Files

File operations are one of the most common Node.js tasks. Always use the promise-based API (fs/promises) instead of callbacks.

import fs from 'node:fs/promises';
import path from 'node:path';

// Read a file
const content = await fs.readFile('data.txt', 'utf-8');
console.log(content);

// Write a file
await fs.writeFile('output.txt', 'Hello, Node.js!');

// Append to a file
await fs.appendFile('log.txt', `[${new Date().toISOString()}] Server started
`);

// Read a directory
const files = await fs.readdir('./src');
console.log(files); // ['index.js', 'utils.js', 'config.js']

// Get file info
const stats = await fs.stat('data.txt');
console.log(stats.size);        // File size in bytes
console.log(stats.isDirectory()); // false
console.log(stats.mtime);       // Last modified time

// Create directories recursively
await fs.mkdir('data/backups/2026', { recursive: true });

// Delete a file
await fs.unlink('temp.txt');

// Copy a file
await fs.copyFile('source.txt', 'destination.txt');

Always use path.join() or path.resolve() instead of string concatenation for file paths. This ensures your code works on both Windows (backslashes) and Unix (forward slashes).

Streams: Handling Large Data

Streams process data in chunks rather than loading everything into memory. This is essential for handling large files, video processing, or high-throughput data pipelines.

import fs from 'node:fs';
import { pipeline } from 'node:stream/promises';
import { createGzip } from 'node:zlib';

// BAD: loads entire 2GB file into memory
const data = await fs.promises.readFile('huge-file.csv', 'utf-8');

// GOOD: streams the file in 64KB chunks
const readStream = fs.createReadStream('huge-file.csv', 'utf-8');
let lineCount = 0;

readStream.on('data', (chunk) => {
  lineCount += chunk.split('
').length - 1;
});

readStream.on('end', () => {
  console.log(`Total lines: ${lineCount}`);
});

// Pipe streams together: read → compress → write
await pipeline(
  fs.createReadStream('huge-file.csv'),
  createGzip(),
  fs.createWriteStream('huge-file.csv.gz')
);
console.log('File compressed successfully');

There are four types of streams in Node.js: Readable (source of data), Writable (destination for data), Duplex (both readable and writable), and Transform (modifies data as it passes through). The pipeline() function is the modern way to chain streams — it handles errors and cleanup automatically.

Environment Variables

Environment variables store configuration that changes between environments (development, staging, production). Never hardcode secrets, API keys, or database URLs in your source code.

// Access environment variables
const port = process.env.PORT || 3000;
const dbUrl = process.env.DATABASE_URL;
const apiKey = process.env.API_KEY;
const nodeEnv = process.env.NODE_ENV || 'development';

if (!dbUrl) {
  console.error('DATABASE_URL is required');
  process.exit(1);
}

// Node.js 22+ supports .env files natively
// Run with: node --env-file=.env server.js

Create a .env file for local development:

# .env - NEVER commit this to Git!
PORT=3000
DATABASE_URL=postgresql://localhost:5432/myapp
API_KEY=sk-your-secret-key
NODE_ENV=development

Add .env to your .gitignore file immediately. Committing secrets to Git is one of the most common and dangerous security mistakes in software development.

The Process Object

The process global provides information about and control over the current Node.js process.

// Command-line arguments
console.log(process.argv);
// ['node', '/path/to/script.js', '--port', '3000']

// Current working directory
console.log(process.cwd());

// Node.js version
console.log(process.version); // v22.x.x

// Memory usage
const mem = process.memoryUsage();
console.log(`Heap: ${Math.round(mem.heapUsed / 1024 / 1024)} MB`);

// Exit gracefully
process.on('SIGTERM', () => {
  console.log('Received SIGTERM, shutting down gracefully...');
  server.close(() => {
    console.log('Server closed');
    process.exit(0);
  });
});

// Handle uncaught exceptions
process.on('uncaughtException', (err) => {
  console.error('Uncaught exception:', err);
  process.exit(1);
});

// Handle unhandled promise rejections
process.on('unhandledRejection', (reason) => {
  console.error('Unhandled rejection:', reason);
  process.exit(1);
});

Always handle SIGTERM and SIGINT signals for graceful shutdown. This is critical in production — when a load balancer sends a shutdown signal, your server should finish processing current requests before closing, not terminate abruptly and drop connections.

Common Node.js Mistakes

Using synchronous file operations in servers: Functions like fs.readFileSync() block the event loop. Use fs.promises or the callback-based API in server code. Synchronous methods are only appropriate for startup scripts and CLI tools.

Not handling errors in async code: Every await can throw. Wrap critical async operations in try/catch blocks, and always add .catch() handlers to Promises that you do not await.

Memory leaks from event listeners: Adding event listeners in a loop or request handler without removing them causes memory leaks. Use once() for one-time listeners, and always clean up in close or disconnect handlers.

Ignoring Node.js version: Always specify your Node.js version in package.json with the engines field. Different LTS versions have different APIs available — code that works on Node 22 may not work on Node 18.

Not using the node: prefix: Since Node.js 16+, you should import built-in modules with the node: prefix (import fs from 'node:fs' instead of import fs from 'fs'). This makes it instantly clear which imports are built-in vs third-party packages, and prevents conflicts with npm packages that share the same name.

Node.js is the gateway to full-stack JavaScript development. Master these basics, and you are ready to build APIs with Express, work with databases, deploy to the cloud, and create production-grade applications — all in the language you already know.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *