Node.js Quick Reference

Setup, core modules, npm, Express.js, middleware, file system, streams, child processes and debugging.

Setup & Versions (nvm)

# Install nvm (Node Version Manager)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash

# List available versions
nvm ls-remote --lts

# Install and use a specific version
nvm install 22              # install latest v22.x LTS
nvm install 20.11.0         # install exact version
nvm use 22                  # switch to v22
nvm alias default 22        # set default version

# Check current version
node --version              # v22.x.x
npm --version               # 10.x.x

# Run a script with a specific version
nvm exec 20 node app.js

# .nvmrc — pin version per project
echo "22" > .nvmrc
nvm use                     # reads .nvmrc automatically
Use the LTS (Long Term Support) version for production. Even-numbered major releases (18, 20, 22) become LTS. Odd-numbered releases (19, 21, 23) are short-lived current releases.

Core Modules (fs, path, http, os)

path

import path from 'node:path';

path.join('/users', 'alex', 'docs', 'file.txt');
// '/users/alex/docs/file.txt'

path.resolve('src', 'utils', 'helpers.js');
// '/absolute/path/to/src/utils/helpers.js'

path.basename('/home/alex/app.js');          // 'app.js'
path.basename('/home/alex/app.js', '.js');   // 'app'
path.dirname('/home/alex/app.js');           // '/home/alex'
path.extname('server.config.js');            // '.js'
path.parse('/home/alex/app.js');
// { root: '/', dir: '/home/alex', base: 'app.js', ext: '.js', name: 'app' }

path.isAbsolute('/usr/bin');   // true
path.isAbsolute('./src');      // false

// ES module equivalent of __dirname
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

http

import http from 'node:http';

const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify({ message: 'Hello from Node.js' }));
});

server.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

os

import os from 'node:os';

os.platform();      // 'linux', 'darwin', 'win32'
os.arch();          // 'x64', 'arm64'
os.cpus().length;   // number of CPU cores
os.totalmem();      // total memory in bytes
os.freemem();       // free memory in bytes
os.homedir();       // '/home/alex'
os.tmpdir();        // '/tmp'
os.hostname();      // machine hostname
os.uptime();        // system uptime in seconds
os.networkInterfaces();  // network interface details

NPM & Package Management

# Initialize a new project
npm init -y                     # creates package.json with defaults

# Install dependencies
npm install express             # production dependency
npm install -D typescript       # dev dependency
npm install -g nodemon          # global install

# Shorthand
npm i express                   # same as npm install
npm i -D vitest                 # same as npm install -D

# Remove a package
npm uninstall express
npm rm express                  # shorthand

# Update
npm outdated                    # check for outdated packages
npm update                      # update within semver ranges
npx npm-check-updates -u       # update package.json to latest

# Scripts (package.json)
# "scripts": {
#   "dev": "node --watch src/index.js",
#   "build": "tsc",
#   "start": "node dist/index.js",
#   "test": "vitest",
#   "lint": "eslint src/"
# }
npm run dev
npm test                        # shorthand for npm run test
npm start                       # shorthand for npm run start

# Lock files
npm ci                          # clean install from lock file (CI/CD)

# View package info
npm info express versions
npm ls --depth=0                # list installed top-level packages

# Security
npm audit                       # check for vulnerabilities
npm audit fix                   # auto-fix vulnerabilities
Semver RangeMeaningExample
^1.2.3Compatible (minor + patch)1.2.3 to <2.0.0
~1.2.3Patch-level changes1.2.3 to <1.3.0
1.2.3Exact versionOnly 1.2.3
*Any versionLatest available

Express.js Basics

import express from 'express';
const app = express();

// Built-in middleware
app.use(express.json());                          // parse JSON bodies
app.use(express.urlencoded({ extended: true }));  // parse form data
app.use(express.static('public'));                // serve static files

// Routes
app.get('/', (req, res) => {
  res.json({ message: 'Hello World' });
});

app.get('/users/:id', (req, res) => {
  const { id } = req.params;
  res.json({ userId: id });
});

app.post('/users', (req, res) => {
  const { name, email } = req.body;
  res.status(201).json({ id: Date.now(), name, email });
});

app.put('/users/:id', (req, res) => {
  res.json({ updated: true, id: req.params.id });
});

app.delete('/users/:id', (req, res) => {
  res.status(204).send();
});

// Query parameters: GET /search?q=node&page=2
app.get('/search', (req, res) => {
  const { q, page = 1 } = req.query;
  res.json({ query: q, page: Number(page) });
});

// Router — modular route handling
const router = express.Router();

router.get('/', (req, res) => res.json({ items: [] }));
router.post('/', (req, res) => res.status(201).json(req.body));
router.get('/:id', (req, res) => res.json({ id: req.params.id }));

app.use('/api/items', router);

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

Middleware Patterns

// Middleware signature: (req, res, next) => void
// Call next() to pass control to the next middleware

// Logging middleware
function logger(req, res, next) {
  const start = Date.now();
  res.on('finish', () => {
    const ms = Date.now() - start;
    console.log(`${req.method} ${req.url} ${res.statusCode} — ${ms}ms`);
  });
  next();
}

app.use(logger);

// Auth middleware
function requireAuth(req, res, next) {
  const token = req.headers.authorization?.split(' ')[1];
  if (!token) {
    return res.status(401).json({ error: 'Authentication required' });
  }
  try {
    req.user = verifyToken(token);
    next();
  } catch {
    res.status(403).json({ error: 'Invalid token' });
  }
}

// Apply to specific routes
app.get('/profile', requireAuth, (req, res) => {
  res.json({ user: req.user });
});

// Apply to all routes in a router
router.use(requireAuth);

// CORS middleware
function cors(req, res, next) {
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type,Authorization');
  if (req.method === 'OPTIONS') return res.status(204).send();
  next();
}

// Rate limiter (simple in-memory)
function rateLimit({ windowMs = 60000, max = 100 } = {}) {
  const hits = new Map();
  return (req, res, next) => {
    const key = req.ip;
    const now = Date.now();
    const record = hits.get(key) || { count: 0, start: now };
    if (now - record.start > windowMs) {
      record.count = 0;
      record.start = now;
    }
    record.count++;
    hits.set(key, record);
    if (record.count > max) {
      return res.status(429).json({ error: 'Too many requests' });
    }
    next();
  };
}

app.use(rateLimit({ windowMs: 60000, max: 100 }));

// Error-handling middleware (4 arguments)
function errorHandler(err, req, res, next) {
  console.error(err.stack);
  res.status(err.status || 500).json({
    error: err.message || 'Internal Server Error'
  });
}

app.use(errorHandler);  // must be registered last

File System Operations

import fs from 'node:fs/promises';
import { createReadStream, createWriteStream } from 'node:fs';

// Read file
const content = await fs.readFile('data.txt', 'utf-8');
const json = JSON.parse(await fs.readFile('config.json', 'utf-8'));
const buffer = await fs.readFile('image.png');  // returns Buffer

// Write file
await fs.writeFile('output.txt', 'Hello, World!');
await fs.writeFile('data.json', JSON.stringify(data, null, 2));

// Append to file
await fs.appendFile('log.txt', `${new Date().toISOString()} — Event\n`);

// Check if file/directory exists
try {
  await fs.access('config.json');
  console.log('File exists');
} catch {
  console.log('File does not exist');
}

// Directory operations
await fs.mkdir('logs', { recursive: true });    // create (nested) directory
await fs.rm('temp', { recursive: true, force: true });  // remove directory

const entries = await fs.readdir('src');                  // list filenames
const detailed = await fs.readdir('src', { withFileTypes: true });
const files = detailed.filter(e => e.isFile());
const dirs = detailed.filter(e => e.isDirectory());

// File info
const stats = await fs.stat('app.js');
stats.size;            // bytes
stats.isFile();        // true
stats.isDirectory();   // false
stats.mtime;           // last modified time

// Copy, rename, delete
await fs.copyFile('src.txt', 'dest.txt');
await fs.rename('old.txt', 'new.txt');
await fs.unlink('temp.txt');                // delete file

// Watch for changes
const watcher = fs.watch('src', { recursive: true });
for await (const event of watcher) {
  console.log(event.eventType, event.filename);
}

Environment Variables

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

// Node.js 20.6+ built-in .env file support
// node --env-file=.env src/index.js
// node --env-file=.env --env-file=.env.local src/index.js

// .env file format:
// PORT=3000
// DATABASE_URL=postgres://user:pass@localhost:5432/mydb
// JWT_SECRET=your-secret-key
// NODE_ENV=development

// Conditional logic based on environment
if (process.env.NODE_ENV === 'production') {
  app.use(helmet());
  app.use(compression());
}

// Required variable check at startup
const required = ['DATABASE_URL', 'JWT_SECRET', 'REDIS_URL'];
for (const key of required) {
  if (!process.env[key]) {
    console.error(`Missing required env variable: ${key}`);
    process.exit(1);
  }
}

// Typed config object
const config = {
  port: parseInt(process.env.PORT || '3000', 10),
  db: {
    url: process.env.DATABASE_URL,
    poolSize: parseInt(process.env.DB_POOL_SIZE || '10', 10),
  },
  jwt: {
    secret: process.env.JWT_SECRET,
    expiresIn: process.env.JWT_EXPIRES_IN || '7d',
  },
  isDev: process.env.NODE_ENV !== 'production',
};
Never commit .env files with real secrets. Add .env to .gitignore and provide a .env.example with placeholder values for documentation.

Child Processes

import { exec, execFile, spawn, fork } from 'node:child_process';
import { promisify } from 'node:util';

const execAsync = promisify(exec);

// exec — runs a shell command, buffers output
const { stdout, stderr } = await execAsync('ls -la');
console.log(stdout);

// execFile — runs a file directly (no shell, safer)
const execFileAsync = promisify(execFile);
const { stdout: gitLog } = await execFileAsync('git', ['log', '--oneline', '-5']);

// spawn — streaming output (for long-running processes)
const child = spawn('npm', ['run', 'build']);

child.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

child.stderr.on('data', (data) => {
  console.error(`stderr: ${data}`);
});

child.on('close', (code) => {
  console.log(`Process exited with code ${code}`);
});

// fork — spawn a new Node.js process with IPC channel
// worker.js
// process.on('message', (msg) => {
//   const result = heavyComputation(msg.data);
//   process.send({ result });
// });

const worker = fork('./worker.js');
worker.send({ data: largeDataSet });
worker.on('message', (msg) => {
  console.log('Result:', msg.result);
});

// Using AbortController to cancel
const controller = new AbortController();
const { signal } = controller;

const child = spawn('long-task', [], { signal });
setTimeout(() => controller.abort(), 10000);

child.on('error', (err) => {
  if (err.code === 'ABORT_ERR') console.log('Process aborted');
});

Streams

import { createReadStream, createWriteStream } from 'node:fs';
import { pipeline } from 'node:stream/promises';
import { createGzip, createGunzip } from 'node:zlib';
import { Transform } from 'node:stream';

// Read stream — reads file in chunks (memory efficient)
const readStream = createReadStream('large-file.txt', { encoding: 'utf-8' });

readStream.on('data', (chunk) => {
  console.log(`Received ${chunk.length} bytes`);
});

readStream.on('end', () => console.log('Done reading'));
readStream.on('error', (err) => console.error(err));

// Write stream
const writeStream = createWriteStream('output.txt');
writeStream.write('Line 1\n');
writeStream.write('Line 2\n');
writeStream.end('Final line\n');

// Pipe — connect streams
createReadStream('input.txt')
  .pipe(createWriteStream('output.txt'));

// Pipeline (preferred) — handles errors and cleanup automatically
await pipeline(
  createReadStream('input.txt'),
  createGzip(),
  createWriteStream('input.txt.gz')
);

// Decompress
await pipeline(
  createReadStream('input.txt.gz'),
  createGunzip(),
  createWriteStream('input-restored.txt')
);

// Custom transform stream
const upperCase = new Transform({
  transform(chunk, encoding, callback) {
    callback(null, chunk.toString().toUpperCase());
  }
});

await pipeline(
  createReadStream('input.txt'),
  upperCase,
  createWriteStream('output-upper.txt')
);

// Line-by-line processing with readline
import { createInterface } from 'node:readline';

const rl = createInterface({
  input: createReadStream('data.csv'),
  crlfDelay: Infinity,
});

for await (const line of rl) {
  const [name, email] = line.split(',');
  console.log({ name, email });
}
Streams are essential for processing large files without loading them entirely into memory. A 2 GB file can be processed with ~64 KB of memory using streams.

Error Handling & Debugging

// Async error handling — always wrap async operations
async function fetchData(url) {
  try {
    const res = await fetch(url);
    if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
    return await res.json();
  } catch (err) {
    if (err.cause) console.error('Caused by:', err.cause);
    throw err;
  }
}

// Custom error classes
class AppError extends Error {
  constructor(message, statusCode = 500, code = 'INTERNAL_ERROR') {
    super(message);
    this.name = 'AppError';
    this.statusCode = statusCode;
    this.code = code;
  }
}

class NotFoundError extends AppError {
  constructor(resource = 'Resource') {
    super(`${resource} not found`, 404, 'NOT_FOUND');
  }
}

// Uncaught exception and rejection handlers
process.on('uncaughtException', (err) => {
  console.error('Uncaught Exception:', err);
  process.exit(1);  // exit — state may be corrupted
});

process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled Rejection at:', promise, 'reason:', reason);
});

// Graceful shutdown
function gracefulShutdown(signal) {
  console.log(`${signal} received. Shutting down gracefully...`);
  server.close(() => {
    console.log('HTTP server closed');
    db.disconnect().then(() => process.exit(0));
  });
  setTimeout(() => process.exit(1), 10000);  // force exit after 10s
}

process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
process.on('SIGINT', () => gracefulShutdown('SIGINT'));

// Built-in debugging
// node --inspect src/index.js          → Chrome DevTools debugger
// node --inspect-brk src/index.js      → break on first line
// node --watch src/index.js            → auto-restart on changes (Node 18+)

// Debug logging with NODE_DEBUG
// NODE_DEBUG=http,net node app.js      → verbose built-in module logging

// Console timing
console.time('db-query');
const result = await db.query('SELECT * FROM users');
console.timeEnd('db-query');  // db-query: 42.123ms

// Structured logging
console.log(JSON.stringify({
  level: 'info',
  message: 'Request processed',
  method: req.method,
  url: req.url,
  status: res.statusCode,
  duration: ms,
  timestamp: new Date().toISOString()
}));