Node.js

Node.js Fundamentals: Server-Side JavaScript Development

Master Node.js fundamentals for backend development. Learn modules, file system, HTTP servers, streams, process management, and building scalable applications.

By JavaScript Document Team
nodejsbackendservermodulesstreams

Node.js enables JavaScript to run on the server, opening up full-stack development possibilities. This comprehensive guide covers Node.js fundamentals, core modules, and patterns for building scalable backend applications.

Node.js Basics

Understanding Node.js Architecture

// Node.js is built on Chrome's V8 JavaScript engine
// Single-threaded event loop with non-blocking I/O

// Event loop demonstration
console.log('Start');

setTimeout(() => {
  console.log('Timeout callback');
}, 0);

setImmediate(() => {
  console.log('Immediate callback');
});

process.nextTick(() => {
  console.log('Next tick callback');
});

console.log('End');

// Output order:
// Start
// End
// Next tick callback
// Immediate callback
// Timeout callback

// Global objects in Node.js
console.log('Current directory:', __dirname);
console.log('Current file:', __filename);
console.log('Process ID:', process.pid);
console.log('Node version:', process.version);
console.log('Platform:', process.platform);

// Environment variables
console.log('Environment:', process.env.NODE_ENV);
console.log('Port:', process.env.PORT || 3000);

// Command line arguments
console.log('Arguments:', process.argv);

// Process events
process.on('exit', (code) => {
  console.log(`Process exiting with code: ${code}`);
});

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

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

Module System

CommonJS Modules

// math.js - Module exports
function add(a, b) {
  return a + b;
}

function subtract(a, b) {
  return a - b;
}

function multiply(a, b) {
  return a * b;
}

function divide(a, b) {
  if (b === 0) {
    throw new Error('Division by zero');
  }
  return a / b;
}

// Named exports
exports.add = add;
exports.subtract = subtract;

// Default export
module.exports = {
  add,
  subtract,
  multiply,
  divide,
  PI: Math.PI,
  calculateCircleArea: (radius) => Math.PI * radius * radius,
};

// Alternative export syntax
module.exports.VERSION = '1.0.0';

// app.js - Module imports
const math = require('./math');
const { add, subtract } = require('./math');

console.log(math.add(5, 3)); // 8
console.log(add(5, 3)); // 8
console.log(math.PI); // 3.141592653589793

// Built-in modules
const fs = require('fs');
const path = require('path');
const http = require('http');
const url = require('url');
const querystring = require('querystring');

// Module caching
console.log('First require:', require('./math'));
console.log('Second require (cached):', require('./math'));

// require.cache contains cached modules
console.log('Cached modules:', Object.keys(require.cache));

// Clear module cache (for testing)
delete require.cache[require.resolve('./math')];

// Module search paths
console.log('Module paths:', require.main.paths);

ES Modules in Node.js

// package.json - Enable ES modules
{
  "type": "module",
  "name": "my-app",
  "version": "1.0.0"
}

// math.mjs - ES module exports
export function add(a, b) {
  return a + b;
}

export function subtract(a, b) {
  return a - b;
}

export const PI = Math.PI;

export default class Calculator {
  constructor() {
    this.history = [];
  }

  add(a, b) {
    const result = a + b;
    this.history.push(`${a} + ${b} = ${result}`);
    return result;
  }

  getHistory() {
    return this.history;
  }
}

// app.mjs - ES module imports
import Calculator, { add, subtract, PI } from './math.mjs';
import { readFile } from 'fs/promises';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';

// ES module equivalents of __dirname and __filename
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

console.log('Current directory:', __dirname);
console.log('Current file:', __filename);

// Dynamic imports
async function loadMathModule() {
  const mathModule = await import('./math.mjs');
  return mathModule;
}

// Top-level await (Node.js 14.8+)
const data = await readFile('config.json', 'utf8');
console.log('Config:', JSON.parse(data));

// Mixed CommonJS and ES modules
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const lodash = require('lodash'); // CommonJS module

File System Operations

Synchronous and Asynchronous File Operations

const fs = require('fs');
const fsPromises = require('fs/promises');
const path = require('path');

// Synchronous file operations (blocking)
try {
  const data = fs.readFileSync('config.json', 'utf8');
  console.log('Config:', JSON.parse(data));
} catch (error) {
  console.error('Error reading file:', error.message);
}

// Asynchronous file operations (callback-based)
fs.readFile('config.json', 'utf8', (error, data) => {
  if (error) {
    console.error('Error reading file:', error.message);
    return;
  }

  try {
    const config = JSON.parse(data);
    console.log('Config:', config);
  } catch (parseError) {
    console.error('Error parsing JSON:', parseError.message);
  }
});

// Promise-based file operations
async function readConfigFile() {
  try {
    const data = await fsPromises.readFile('config.json', 'utf8');
    const config = JSON.parse(data);
    return config;
  } catch (error) {
    console.error('Error reading config:', error.message);
    throw error;
  }
}

// File operations class
class FileManager {
  constructor(baseDir = './data') {
    this.baseDir = baseDir;
    this.ensureDirectory();
  }

  async ensureDirectory() {
    try {
      await fsPromises.mkdir(this.baseDir, { recursive: true });
    } catch (error) {
      console.error('Error creating directory:', error.message);
    }
  }

  async writeFile(filename, data) {
    const filePath = path.join(this.baseDir, filename);

    try {
      await fsPromises.writeFile(filePath, data, 'utf8');
      console.log(`File written: ${filePath}`);
    } catch (error) {
      console.error(`Error writing file ${filename}:`, error.message);
      throw error;
    }
  }

  async readFile(filename) {
    const filePath = path.join(this.baseDir, filename);

    try {
      const data = await fsPromises.readFile(filePath, 'utf8');
      return data;
    } catch (error) {
      if (error.code === 'ENOENT') {
        throw new Error(`File not found: ${filename}`);
      }
      throw error;
    }
  }

  async deleteFile(filename) {
    const filePath = path.join(this.baseDir, filename);

    try {
      await fsPromises.unlink(filePath);
      console.log(`File deleted: ${filePath}`);
    } catch (error) {
      if (error.code === 'ENOENT') {
        console.log(`File does not exist: ${filename}`);
        return;
      }
      throw error;
    }
  }

  async listFiles() {
    try {
      const files = await fsPromises.readdir(this.baseDir);
      const fileStats = await Promise.all(
        files.map(async (file) => {
          const filePath = path.join(this.baseDir, file);
          const stats = await fsPromises.stat(filePath);

          return {
            name: file,
            size: stats.size,
            isDirectory: stats.isDirectory(),
            modified: stats.mtime,
            created: stats.birthtime,
          };
        })
      );

      return fileStats;
    } catch (error) {
      console.error('Error listing files:', error.message);
      throw error;
    }
  }

  async copyFile(source, destination) {
    const sourcePath = path.join(this.baseDir, source);
    const destPath = path.join(this.baseDir, destination);

    try {
      await fsPromises.copyFile(sourcePath, destPath);
      console.log(`File copied: ${source} -> ${destination}`);
    } catch (error) {
      console.error(`Error copying file:`, error.message);
      throw error;
    }
  }

  async watchFile(filename, callback) {
    const filePath = path.join(this.baseDir, filename);

    const watcher = fs.watch(filePath, (eventType, changedFilename) => {
      console.log(`File ${changedFilename} changed: ${eventType}`);
      callback(eventType, changedFilename);
    });

    return watcher;
  }
}

// Usage example
async function demonstrateFileOperations() {
  const fileManager = new FileManager('./data');

  try {
    // Write a file
    await fileManager.writeFile('test.txt', 'Hello, Node.js!');

    // Read the file
    const content = await fileManager.readFile('test.txt');
    console.log('File content:', content);

    // List all files
    const files = await fileManager.listFiles();
    console.log('Files:', files);

    // Copy the file
    await fileManager.copyFile('test.txt', 'test-copy.txt');

    // Watch for changes
    const watcher = await fileManager.watchFile('test.txt', (eventType) => {
      console.log(`File event: ${eventType}`);
    });

    // Clean up
    setTimeout(() => {
      watcher.close();
    }, 5000);
  } catch (error) {
    console.error('File operation failed:', error.message);
  }
}

HTTP Server Development

Basic HTTP Server

const http = require('http');
const url = require('url');
const querystring = require('querystring');

// Basic HTTP server
const server = http.createServer((req, res) => {
  const { method, url: requestUrl, headers } = req;
  const parsedUrl = url.parse(requestUrl, true);
  const { pathname, query } = parsedUrl;

  console.log(`${method} ${pathname}`);

  // Set CORS headers
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type');

  // Handle preflight requests
  if (method === 'OPTIONS') {
    res.writeHead(200);
    res.end();
    return;
  }

  // Route handling
  if (pathname === '/') {
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.end('<h1>Welcome to Node.js Server</h1>');
  } else if (pathname === '/api/users' && method === 'GET') {
    handleGetUsers(req, res, query);
  } else if (pathname === '/api/users' && method === 'POST') {
    handleCreateUser(req, res);
  } else if (pathname.startsWith('/api/users/') && method === 'GET') {
    const userId = pathname.split('/')[3];
    handleGetUser(req, res, userId);
  } else {
    res.writeHead(404, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ error: 'Not found' }));
  }
});

// Mock database
let users = [
  { id: 1, name: 'John Doe', email: 'john@example.com' },
  { id: 2, name: 'Jane Smith', email: 'jane@example.com' },
];

function handleGetUsers(req, res, query) {
  const { page = 1, limit = 10, search } = query;

  let filteredUsers = users;

  if (search) {
    filteredUsers = users.filter(
      (user) =>
        user.name.toLowerCase().includes(search.toLowerCase()) ||
        user.email.toLowerCase().includes(search.toLowerCase())
    );
  }

  const startIndex = (page - 1) * limit;
  const endIndex = startIndex + parseInt(limit);
  const paginatedUsers = filteredUsers.slice(startIndex, endIndex);

  const response = {
    users: paginatedUsers,
    pagination: {
      page: parseInt(page),
      limit: parseInt(limit),
      total: filteredUsers.length,
      pages: Math.ceil(filteredUsers.length / limit),
    },
  };

  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify(response));
}

function handleGetUser(req, res, userId) {
  const user = users.find((u) => u.id === parseInt(userId));

  if (!user) {
    res.writeHead(404, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ error: 'User not found' }));
    return;
  }

  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify(user));
}

function handleCreateUser(req, res) {
  let body = '';

  req.on('data', (chunk) => {
    body += chunk.toString();
  });

  req.on('end', () => {
    try {
      const userData = JSON.parse(body);

      if (!userData.name || !userData.email) {
        res.writeHead(400, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({ error: 'Name and email are required' }));
        return;
      }

      const newUser = {
        id: users.length + 1,
        name: userData.name,
        email: userData.email,
      };

      users.push(newUser);

      res.writeHead(201, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify(newUser));
    } catch (error) {
      res.writeHead(400, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ error: 'Invalid JSON' }));
    }
  });

  req.on('error', (error) => {
    console.error('Request error:', error);
    res.writeHead(500, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ error: 'Internal server error' }));
  });
}

const PORT = process.env.PORT || 3000;

server.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

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

Advanced HTTP Server with Express-like Features

// Simple Express-like framework
class SimpleExpress {
  constructor() {
    this.routes = {
      GET: {},
      POST: {},
      PUT: {},
      DELETE: {},
    };
    this.middleware = [];
  }

  use(middleware) {
    this.middleware.push(middleware);
  }

  get(path, handler) {
    this.routes.GET[path] = handler;
  }

  post(path, handler) {
    this.routes.POST[path] = handler;
  }

  put(path, handler) {
    this.routes.PUT[path] = handler;
  }

  delete(path, handler) {
    this.routes.DELETE[path] = handler;
  }

  async handleRequest(req, res) {
    // Add helper methods to response
    res.json = (data) => {
      res.setHeader('Content-Type', 'application/json');
      res.end(JSON.stringify(data));
    };

    res.status = (code) => {
      res.statusCode = code;
      return res;
    };

    // Parse request body for POST/PUT requests
    if (req.method === 'POST' || req.method === 'PUT') {
      await this.parseBody(req);
    }

    // Run middleware
    for (const middleware of this.middleware) {
      await new Promise((resolve, reject) => {
        middleware(req, res, (error) => {
          if (error) reject(error);
          else resolve();
        });
      });
    }

    // Find and execute route handler
    const handler = this.routes[req.method]?.[req.url];

    if (handler) {
      try {
        await handler(req, res);
      } catch (error) {
        console.error('Route handler error:', error);
        res.status(500).json({ error: 'Internal server error' });
      }
    } else {
      res.status(404).json({ error: 'Not found' });
    }
  }

  async parseBody(req) {
    return new Promise((resolve) => {
      let body = '';

      req.on('data', (chunk) => {
        body += chunk.toString();
      });

      req.on('end', () => {
        try {
          req.body = JSON.parse(body);
        } catch (error) {
          req.body = {};
        }
        resolve();
      });
    });
  }

  listen(port, callback) {
    const server = http.createServer((req, res) => {
      this.handleRequest(req, res);
    });

    server.listen(port, callback);
    return server;
  }
}

// Usage example
const app = new SimpleExpress();

// Middleware
app.use((req, res, next) => {
  console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
  next();
});

app.use((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');
  next();
});

// Routes
app.get('/', (req, res) => {
  res.json({ message: 'Welcome to Simple Express!' });
});

app.get('/api/users', (req, res) => {
  res.json({ users: users });
});

app.post('/api/users', (req, res) => {
  const { name, email } = req.body;

  if (!name || !email) {
    return res.status(400).json({ error: 'Name and email are required' });
  }

  const newUser = {
    id: users.length + 1,
    name,
    email,
  };

  users.push(newUser);
  res.status(201).json(newUser);
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Streams and Buffers

Working with Streams

const fs = require('fs');
const { Readable, Writable, Transform, pipeline } = require('stream');
const { promisify } = require('util');

// Readable stream
class NumberStream extends Readable {
  constructor(options) {
    super(options);
    this.current = 1;
    this.max = options.max || 10;
  }

  _read() {
    if (this.current <= this.max) {
      this.push(`Number: ${this.current}\n`);
      this.current++;
    } else {
      this.push(null); // End the stream
    }
  }
}

// Writable stream
class LogStream extends Writable {
  constructor(options) {
    super(options);
    this.filename = options.filename || 'output.log';
  }

  _write(chunk, encoding, callback) {
    const timestamp = new Date().toISOString();
    const logEntry = `[${timestamp}] ${chunk.toString()}`;

    fs.appendFile(this.filename, logEntry, callback);
  }
}

// Transform stream
class UpperCaseTransform extends Transform {
  _transform(chunk, encoding, callback) {
    const transformed = chunk.toString().toUpperCase();
    callback(null, transformed);
  }
}

// File processing with streams
class FileProcessor {
  static async processLargeFile(inputFile, outputFile) {
    const readStream = fs.createReadStream(inputFile, { encoding: 'utf8' });
    const writeStream = fs.createWriteStream(outputFile);
    const transform = new UpperCaseTransform();

    const pipelineAsync = promisify(pipeline);

    try {
      await pipelineAsync(readStream, transform, writeStream);
      console.log('File processing completed');
    } catch (error) {
      console.error('File processing failed:', error);
    }
  }

  static async copyFileWithProgress(source, destination) {
    const stats = await fs.promises.stat(source);
    const totalSize = stats.size;
    let processedSize = 0;

    const readStream = fs.createReadStream(source);
    const writeStream = fs.createWriteStream(destination);

    readStream.on('data', (chunk) => {
      processedSize += chunk.length;
      const progress = ((processedSize / totalSize) * 100).toFixed(2);
      process.stdout.write(`\rProgress: ${progress}%`);
    });

    readStream.on('end', () => {
      console.log('\nFile copy completed');
    });

    readStream.pipe(writeStream);
  }

  static createBackupStream(filename) {
    const readStream = fs.createReadStream(filename);
    const backupStream = fs.createWriteStream(`${filename}.backup`);

    readStream.pipe(backupStream);

    return new Promise((resolve, reject) => {
      backupStream.on('finish', resolve);
      backupStream.on('error', reject);
    });
  }
}

// Stream utilities
class StreamUtils {
  static async streamToString(stream) {
    const chunks = [];

    return new Promise((resolve, reject) => {
      stream.on('data', (chunk) => chunks.push(chunk));
      stream.on('error', reject);
      stream.on('end', () => resolve(Buffer.concat(chunks).toString()));
    });
  }

  static createLineStream() {
    let buffer = '';

    return new Transform({
      transform(chunk, encoding, callback) {
        buffer += chunk.toString();
        const lines = buffer.split('\n');

        // Keep the last incomplete line in buffer
        buffer = lines.pop();

        // Emit complete lines
        for (const line of lines) {
          this.push(line + '\n');
        }

        callback();
      },

      flush(callback) {
        if (buffer) {
          this.push(buffer);
        }
        callback();
      },
    });
  }

  static createThrottleStream(delay = 100) {
    return new Transform({
      transform(chunk, encoding, callback) {
        setTimeout(() => {
          callback(null, chunk);
        }, delay);
      },
    });
  }
}

// Buffer operations
class BufferUtils {
  static createBuffer(data, encoding = 'utf8') {
    return Buffer.from(data, encoding);
  }

  static concatenateBuffers(buffers) {
    return Buffer.concat(buffers);
  }

  static compareBuffers(buf1, buf2) {
    return Buffer.compare(buf1, buf2);
  }

  static bufferToHex(buffer) {
    return buffer.toString('hex');
  }

  static hexToBuffer(hex) {
    return Buffer.from(hex, 'hex');
  }

  static bufferToBase64(buffer) {
    return buffer.toString('base64');
  }

  static base64ToBuffer(base64) {
    return Buffer.from(base64, 'base64');
  }
}

Process Management and Child Processes

const { spawn, exec, execFile, fork } = require('child_process');
const { promisify } = require('util');

const execAsync = promisify(exec);
const execFileAsync = promisify(execFile);

// Process management
class ProcessManager {
  constructor() {
    this.processes = new Map();
  }

  async executeCommand(command, options = {}) {
    try {
      const { stdout, stderr } = await execAsync(command, options);
      return { stdout, stderr, success: true };
    } catch (error) {
      return {
        stdout: error.stdout,
        stderr: error.stderr,
        success: false,
        error: error.message,
      };
    }
  }

  spawnProcess(command, args = [], options = {}) {
    const child = spawn(command, args, {
      stdio: 'pipe',
      ...options,
    });

    const processId = Date.now() + Math.random();
    this.processes.set(processId, child);

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

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

    child.on('close', (code) => {
      console.log(`[${processId}] Process exited with code ${code}`);
      this.processes.delete(processId);
    });

    child.on('error', (error) => {
      console.error(`[${processId}] Process error:`, error);
      this.processes.delete(processId);
    });

    return { processId, child };
  }

  killProcess(processId) {
    const child = this.processes.get(processId);
    if (child) {
      child.kill('SIGTERM');
      return true;
    }
    return false;
  }

  killAllProcesses() {
    for (const [processId, child] of this.processes) {
      child.kill('SIGTERM');
    }
    this.processes.clear();
  }

  // Fork Node.js processes
  forkWorker(scriptPath, args = [], options = {}) {
    const child = fork(scriptPath, args, options);

    child.on('message', (message) => {
      console.log('Received from worker:', message);
    });

    child.on('error', (error) => {
      console.error('Worker error:', error);
    });

    child.on('exit', (code, signal) => {
      console.log(`Worker exited with code ${code} and signal ${signal}`);
    });

    return child;
  }
}

// Worker script example (worker.js)
/*
process.on('message', (message) => {
  console.log('Worker received:', message);
  
  if (message.type === 'compute') {
    const result = heavyComputation(message.data);
    process.send({ type: 'result', data: result });
  }
});

function heavyComputation(data) {
  // Simulate heavy computation
  let result = 0;
  for (let i = 0; i < data.iterations; i++) {
    result += Math.random();
  }
  return result;
}

process.send({ type: 'ready' });
*/

// Cluster management
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log(`Master ${process.pid} is running`);

  // Fork workers
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died`);

    // Restart worker
    cluster.fork();
  });

  cluster.on('online', (worker) => {
    console.log(`Worker ${worker.process.pid} is online`);
  });
} else {
  // Worker process
  const server = http.createServer((req, res) => {
    res.writeHead(200);
    res.end(`Hello from worker ${process.pid}\n`);
  });

  server.listen(3000, () => {
    console.log(`Worker ${process.pid} started`);
  });
}

// Process monitoring
class ProcessMonitor {
  constructor() {
    this.startTime = Date.now();
    this.setupMonitoring();
  }

  setupMonitoring() {
    // Memory usage monitoring
    setInterval(() => {
      const memUsage = process.memoryUsage();
      console.log('Memory usage:', {
        rss: `${(memUsage.rss / 1024 / 1024).toFixed(2)} MB`,
        heapTotal: `${(memUsage.heapTotal / 1024 / 1024).toFixed(2)} MB`,
        heapUsed: `${(memUsage.heapUsed / 1024 / 1024).toFixed(2)} MB`,
        external: `${(memUsage.external / 1024 / 1024).toFixed(2)} MB`,
      });
    }, 30000); // Every 30 seconds

    // CPU usage monitoring
    setInterval(() => {
      const cpuUsage = process.cpuUsage();
      console.log('CPU usage:', {
        user: `${(cpuUsage.user / 1000).toFixed(2)} ms`,
        system: `${(cpuUsage.system / 1000).toFixed(2)} ms`,
      });
    }, 30000);
  }

  getUptime() {
    return Date.now() - this.startTime;
  }

  getSystemInfo() {
    return {
      platform: process.platform,
      arch: process.arch,
      nodeVersion: process.version,
      pid: process.pid,
      ppid: process.ppid,
      uptime: this.getUptime(),
      workingDirectory: process.cwd(),
      execPath: process.execPath,
    };
  }
}

// Signal handling
process.on('SIGINT', () => {
  console.log('Received SIGINT, performing graceful shutdown...');
  // Perform cleanup operations
  process.exit(0);
});

process.on('SIGTERM', () => {
  console.log('Received SIGTERM, performing graceful shutdown...');
  // Perform cleanup operations
  process.exit(0);
});

// Uncaught exception handling
process.on('uncaughtException', (error) => {
  console.error('Uncaught Exception:', error);
  // Log error and exit gracefully
  process.exit(1);
});

process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled Rejection at:', promise, 'reason:', reason);
  // Log error but don't exit immediately
});

Node.js Best Practices

// Error handling patterns
class ErrorHandler {
  static createError(message, statusCode = 500, code = 'INTERNAL_ERROR') {
    const error = new Error(message);
    error.statusCode = statusCode;
    error.code = code;
    return error;
  }

  static asyncHandler(fn) {
    return (req, res, next) => {
      Promise.resolve(fn(req, res, next)).catch(next);
    };
  }

  static globalErrorHandler(error, req, res, next) {
    const statusCode = error.statusCode || 500;
    const message = error.message || 'Internal Server Error';

    console.error('Error:', {
      message,
      stack: error.stack,
      url: req.url,
      method: req.method,
      timestamp: new Date().toISOString(),
    });

    res.status(statusCode).json({
      error: {
        message,
        code: error.code,
        ...(process.env.NODE_ENV === 'development' && { stack: error.stack }),
      },
    });
  }
}

// Configuration management
class Config {
  constructor() {
    this.config = {
      port: process.env.PORT || 3000,
      nodeEnv: process.env.NODE_ENV || 'development',
      database: {
        host: process.env.DB_HOST || 'localhost',
        port: process.env.DB_PORT || 5432,
        name: process.env.DB_NAME || 'myapp',
        user: process.env.DB_USER || 'postgres',
        password: process.env.DB_PASSWORD || '',
      },
      jwt: {
        secret: process.env.JWT_SECRET || 'default-secret',
        expiresIn: process.env.JWT_EXPIRES_IN || '7d',
      },
      redis: {
        host: process.env.REDIS_HOST || 'localhost',
        port: process.env.REDIS_PORT || 6379,
      },
    };

    this.validate();
  }

  validate() {
    const required = ['JWT_SECRET', 'DB_PASSWORD'];

    const missing = required.filter((key) => !process.env[key]);

    if (missing.length > 0) {
      throw new Error(
        `Missing required environment variables: ${missing.join(', ')}`
      );
    }
  }

  get(key) {
    return this.config[key];
  }

  isDevelopment() {
    return this.config.nodeEnv === 'development';
  }

  isProduction() {
    return this.config.nodeEnv === 'production';
  }
}

// Logging utility
class Logger {
  constructor() {
    this.logLevel = process.env.LOG_LEVEL || 'info';
    this.levels = {
      error: 0,
      warn: 1,
      info: 2,
      debug: 3,
    };
  }

  log(level, message, meta = {}) {
    if (this.levels[level] <= this.levels[this.logLevel]) {
      const logEntry = {
        timestamp: new Date().toISOString(),
        level,
        message,
        ...meta,
      };

      console.log(JSON.stringify(logEntry));
    }
  }

  error(message, meta) {
    this.log('error', message, meta);
  }

  warn(message, meta) {
    this.log('warn', message, meta);
  }

  info(message, meta) {
    this.log('info', message, meta);
  }

  debug(message, meta) {
    this.log('debug', message, meta);
  }
}

// Database connection pool
class DatabasePool {
  constructor(config) {
    this.config = config;
    this.connections = [];
    this.maxConnections = config.maxConnections || 10;
    this.activeConnections = 0;
  }

  async getConnection() {
    if (this.connections.length > 0) {
      return this.connections.pop();
    }

    if (this.activeConnections < this.maxConnections) {
      const connection = await this.createConnection();
      this.activeConnections++;
      return connection;
    }

    // Wait for available connection
    return new Promise((resolve) => {
      const checkForConnection = () => {
        if (this.connections.length > 0) {
          resolve(this.connections.pop());
        } else {
          setTimeout(checkForConnection, 50);
        }
      };
      checkForConnection();
    });
  }

  releaseConnection(connection) {
    this.connections.push(connection);
  }

  async createConnection() {
    // Implement actual database connection
    return {
      query: async (sql, params) => {
        // Mock query implementation
        console.log('Executing query:', sql, params);
        return { rows: [] };
      },
      close: () => {
        this.activeConnections--;
      },
    };
  }

  async closeAll() {
    this.connections.forEach((conn) => conn.close());
    this.connections = [];
    this.activeConnections = 0;
  }
}

// Application lifecycle management
class Application {
  constructor() {
    this.config = new Config();
    this.logger = new Logger();
    this.isShuttingDown = false;

    this.setupGracefulShutdown();
  }

  setupGracefulShutdown() {
    const gracefulShutdown = async (signal) => {
      if (this.isShuttingDown) return;

      this.logger.info(`Received ${signal}, starting graceful shutdown`);
      this.isShuttingDown = true;

      try {
        // Close server
        if (this.server) {
          await new Promise((resolve) => {
            this.server.close(resolve);
          });
        }

        // Close database connections
        if (this.database) {
          await this.database.closeAll();
        }

        this.logger.info('Graceful shutdown completed');
        process.exit(0);
      } catch (error) {
        this.logger.error('Error during shutdown:', { error: error.message });
        process.exit(1);
      }
    };

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

  async start() {
    try {
      this.logger.info('Starting application');

      // Initialize database
      this.database = new DatabasePool(this.config.get('database'));

      // Start server
      this.server = http.createServer(this.requestHandler.bind(this));

      const port = this.config.get('port');
      this.server.listen(port, () => {
        this.logger.info(`Server started on port ${port}`);
      });
    } catch (error) {
      this.logger.error('Failed to start application:', {
        error: error.message,
      });
      process.exit(1);
    }
  }

  async requestHandler(req, res) {
    // Basic request handling
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(
      JSON.stringify({
        message: 'Node.js application running',
        timestamp: new Date().toISOString(),
      })
    );
  }
}

// Start application
const app = new Application();
app.start();

Conclusion

Node.js fundamentals provide the foundation for building scalable server-side JavaScript applications. Understanding the event loop, module system, file operations, HTTP servers, streams, and process management enables you to create robust backend services. By following best practices for error handling, configuration management, logging, and application lifecycle, you can build production-ready Node.js applications that are maintainable, scalable, and reliable.