Node.js¶
JavaScript runtime built on Chrome's V8 engine. Event-driven, non-blocking I/O makes it ideal for I/O-heavy applications (APIs, real-time apps, microservices). Single-threaded event loop handles thousands of concurrent connections. npm is the largest package ecosystem (2M+ packages). Fast for I/O, terrible for CPU-intensive tasks.
2026 Update
Node.js 22 LTS brings native TypeScript support (no transpilation needed). V8 performance improvements (30% faster JSON parsing). ESM (ES Modules) finally standard (CommonJS legacy). Bun and Deno are serious alternatives. pnpm replaces npm (faster, disk-efficient). Serverless Node.js dominates (AWS Lambda, Vercel, Cloudflare Workers).
Quick Hits¶
// Variables (prefer const/let, avoid var)
const name = "Node.js"; // Immutable
let version = 22; // Mutable
// var is legacy (function-scoped, hoisting issues)
// String templates (backticks)
const greeting = `Hello, ${name} v${version}`; // (1)!
// Arrow functions (modern syntax)
const add = (a, b) => a + b;
const greet = name => `Hello, ${name}`; // Single param, no parens
// Traditional function
function multiply(a, b) {
return a * b;
}
// Destructuring (clean object/array extraction)
const user = { id: 1, name: "Alice", email: "alice@example.com" };
const { name: userName, email } = user; // (2)!
const numbers = [1, 2, 3, 4, 5];
const [first, second, ...rest] = numbers; // rest = [3, 4, 5]
// Spread operator (copy, merge)
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 }; // { a: 1, b: 2, c: 3 }
// Promises (async operations)
const fetchUser = (id) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id > 0) {
resolve({ id, name: "Alice" });
} else {
reject(new Error("Invalid ID"));
}
}, 1000);
});
}; // (3)!
// Async/await (modern promise syntax)
async function getUser(id) {
try {
const user = await fetchUser(id);
console.log(user);
return user;
} catch (error) {
console.error("Error:", error.message);
}
} // (4)!
// Array methods (functional style)
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2); // [2, 4, 6, 8, 10]
const evens = numbers.filter(n => n % 2 === 0); // [2, 4]
const sum = numbers.reduce((acc, n) => acc + n, 0); // 15
// Classes (ES6+)
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
greet() {
return `Hello, ${this.name}`;
}
static createGuest() {
return new User("Guest", "guest@example.com");
}
}
// Modules (ESM - modern Node.js)
// math.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
export default class Calculator { } // (5)!
// app.js
import Calculator, { add, subtract } from './math.js';
// OR CommonJS (legacy)
const { add, subtract } = require('./math.js');
- Template literals support multi-line strings and expressions
- Destructuring avoids repetitive
user.name,user.email - Promises handle async operations (avoid callback hell)
- Async/await makes async code look synchronous (cleaner)
- ESM is modern (use
"type": "module"in package.json)
Real talk:
- Always use
constunless you need to reassign (prefer immutability) - Arrow functions don't bind
this(matters in classes/callbacks) - Async/await is mandatory for modern Node.js (no more callback hell)
- ESM is the future (CommonJS is legacy but still common)
- Array methods (
map,filter,reduce) are idiomatic JavaScript
// Express.js REST API (most popular framework)
import express from 'express';
const app = express();
app.use(express.json()); // Parse JSON bodies // (1)!
// GET endpoint
app.get('/api/users', async (req, res) => {
const users = await db.query('SELECT * FROM users');
res.json(users);
});
// POST endpoint with validation
app.post('/api/users', async (req, res) => {
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({ error: 'Missing fields' });
}
const user = await db.insert({ name, email });
res.status(201).json(user);
});
app.listen(3000, () => {
console.log('Server running on port 3000');
}); // (2)!
// Fastify (faster alternative to Express)
import Fastify from 'fastify';
const fastify = Fastify({ logger: true });
fastify.get('/api/users', async (request, reply) => {
const users = await db.query('SELECT * FROM users');
return users; // Auto-serialized to JSON
});
await fastify.listen({ port: 3000 }); // (3)!
// Database connection (PostgreSQL with pg)
import pg from 'pg';
const { Pool } = pg;
const pool = new Pool({
host: 'localhost',
port: 5432,
database: 'mydb',
user: 'postgres',
password: 'password',
max: 20, // Connection pool size
});
// Query with parameterized statement (prevent SQL injection)
const getUserById = async (id) => {
const result = await pool.query(
'SELECT * FROM users WHERE id = $1',
[id] // Parameterized query
);
return result.rows[0];
}; // (4)!
// File operations (fs/promises - async API)
import { readFile, writeFile, mkdir } from 'fs/promises';
import { join } from 'path';
// Read file
const content = await readFile('data.json', 'utf-8');
const data = JSON.parse(content);
// Write file (create directories if needed)
await mkdir('output', { recursive: true });
await writeFile('output/result.json', JSON.stringify(data, null, 2));
// HTTP requests (fetch is built-in in Node.js 18+)
const response = await fetch('https://api.example.com/users');
const users = await response.json(); // (5)!
// Or use axios (more features)
import axios from 'axios';
const { data } = await axios.get('https://api.example.com/users', {
headers: { 'Authorization': 'Bearer token' }
});
// Environment variables (.env file)
import 'dotenv/config'; // Load .env file
const dbUrl = process.env.DATABASE_URL;
const apiKey = process.env.API_KEY; // (6)!
// Error handling middleware (Express)
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Internal server error' });
});
// Graceful shutdown
process.on('SIGTERM', async () => {
console.log('SIGTERM received, closing server...');
await pool.end(); // Close DB connections
server.close(() => {
console.log('Server closed');
process.exit(0);
});
}); // (7)!
// Testing with Vitest (modern, fast)
import { describe, it, expect } from 'vitest';
describe('add function', () => {
it('adds two numbers', () => {
expect(add(2, 3)).toBe(5);
});
it('handles negative numbers', () => {
expect(add(-1, 1)).toBe(0);
});
}); // (8)!
// WebSockets (real-time communication)
import { WebSocketServer } from 'ws';
const wss = new WebSocketServer({ port: 8080 });
wss.on('connection', (ws) => {
ws.on('message', (message) => {
// Broadcast to all clients
wss.clients.forEach((client) => {
if (client.readyState === 1) { // OPEN
client.send(message);
}
});
});
}); // (9)!
// Streams (memory-efficient file processing)
import { createReadStream, createWriteStream } from 'fs';
import { pipeline } from 'stream/promises';
import { createGzip } from 'zlib';
// Compress file (stream avoids loading entire file in memory)
await pipeline(
createReadStream('large-file.txt'),
createGzip(),
createWriteStream('large-file.txt.gz')
); // (10)!
- Express middleware parses JSON request bodies
- Express simple but slower than Fastify
- Fastify 2-3x faster than Express (native schema validation)
- Parameterized queries prevent SQL injection
- Fetch API built-in since Node.js 18 (no more axios required)
- dotenv loads environment variables from .env file
- Graceful shutdown prevents data loss (close connections cleanly)
- Vitest faster than Jest (ESM-native, watch mode)
- WebSockets enable real-time bidirectional communication
- Streams handle large files without loading into memory
Why this works:
- Express/Fastify handle routing, middleware, HTTP automatically
- Connection pooling reuses database connections (performance)
- Async/await makes async code readable (no callback hell)
- Parameterized queries prevent SQL injection
- Graceful shutdown prevents data corruption
- Streams process large files efficiently (low memory usage)
Best Practices
- Use pnpm - 2x faster than npm, saves disk space (deduplicated)
- TypeScript - Type safety catches bugs at compile time
- ESM over CommonJS - Modern module system (set
"type": "module") - Fastify over Express - 2-3x faster, native async/await
- Environment variables - Use dotenv, never commit secrets
- Error handling - Always handle promise rejections (avoid unhandled)
- Graceful shutdown - Handle SIGTERM for zero-downtime deploys
- Logging - Use pino or winston (not console.log in production)
Performance
- Single-threaded - CPU-intensive tasks block event loop (use worker threads)
- Cluster mode - Run multiple Node processes (utilize all CPU cores)
- Event loop blocking - Avoid synchronous operations (readFileSync, etc.)
- Memory leaks - Node doesn't GC properly if you hold references
- Connection pooling - Reuse DB connections (don't create per-request)
- Caching - Use Redis for frequently accessed data
- Profiling - Use
node --inspect+ Chrome DevTools for bottlenecks
Modern Node.js Tools
- pnpm - Fast, disk-efficient package manager (replaces npm)
- tsx - Run TypeScript directly (no build step)
- Vitest - Fast test runner (ESM-native, 10x faster than Jest)
- esbuild - Bundle JavaScript/TypeScript (100x faster than webpack)
- Biome - Linter + formatter (Rust-based, replaces ESLint + Prettier)
- Drizzle ORM - Type-safe SQL (better than Prisma for performance)
- pino - Fast JSON logger (5x faster than winston)
Common Gotchas
- Callback hell - Use async/await (avoid nested callbacks)
- Unhandled promise rejections - Always
.catch()ortry/catch - Blocking event loop - Synchronous operations freeze server
- Memory leaks - Closures, event listeners, global variables
- require() vs import - Can't mix CommonJS and ESM easily
- this binding - Arrow functions don't bind
this(use regular functions in classes) - package.json scripts - Use
&&for sequential,&for parallel (Unix syntax)
Framework Ecosystem
- Web - Express (popular), Fastify (fast), Hono (modern)
- Full-stack - Next.js, Remix, Nuxt.js, SvelteKit
- API - tRPC (type-safe APIs), GraphQL (Apollo, Pothos)
- Database - Prisma (ORM), Drizzle (type-safe SQL), Kysely (query builder)
- Testing - Vitest, Jest, Playwright (E2E), Supertest (API testing)
- Real-time - Socket.io, ws (WebSockets), Server-Sent Events
Package Management¶
Modern Approach (pnpm - Recommended)¶
# Install pnpm (fast, disk-efficient)
npm install -g pnpm
# Initialize project
pnpm init
# Install packages
pnpm add express
pnpm add -D typescript @types/node # Dev dependencies
# Install all dependencies
pnpm install
# Run scripts
pnpm start
pnpm test
# Update packages
pnpm update
pnpm outdated # Check for updates
Traditional Approach (npm)¶
# Initialize project
npm init -y
# Install packages
npm install express
npm install --save-dev typescript
# Install from package.json
npm install
# Run scripts
npm start
npm run dev
# Update packages
npm update
npm outdated
Alternative Runtime (Bun - Fastest)¶
# Install Bun (3x faster than Node.js)
curl -fsSL https://bun.sh/install | bash
# Run TypeScript directly (no transpilation)
bun run index.ts
# Install packages (10x faster than npm)
bun install
# Run tests
bun test
Learning Resources¶
Free Resources¶
- Node.js Official Docs - Start here for APIs
- MDN JavaScript Guide - JavaScript fundamentals
- Node.js Best Practices - Production patterns
- Express.js Guide - Most popular framework
- NPM Package Search - Find packages
Practice Projects¶
Beginner
- CLI tool - Todo list, file renamer, URL shortener
- REST API - CRUD operations with Express + PostgreSQL
- File processor - Read CSV, transform data, write JSON
- Web scraper - Cheerio for HTML parsing
Intermediate
- Real-time chat - WebSockets with Socket.io
- Authentication API - JWT tokens, bcrypt passwords, refresh tokens
- Microservice - Fastify + PostgreSQL + Redis caching
- Image processor - Sharp library for resizing/optimization
Advanced
- GraphQL API - Apollo Server with DataLoader
- Event-driven architecture - RabbitMQ or Kafka consumers
- Serverless functions - AWS Lambda or Vercel functions
- npm package - Create and publish reusable library
Worth Checking¶
-
Official Docs
-
Essential Tools
-
Popular Frameworks
-
Community
Last Updated: 2026-02-02 | Vibe Check: Dominant - Node.js powers most backend JavaScript. npm is the largest package ecosystem (2M+ packages). Fast for I/O (APIs, real-time), terrible for CPU work. Serverless Node.js everywhere (Lambda, Vercel, Cloudflare Workers). Bun and Deno are viable alternatives but Node.js still dominates.
Tags: nodejs, javascript, backend, server-side, runtime