Master advanced Node.js patterns, design patterns, debugging techniques, error handling strategies, and production best practices.
Master advanced Node.js patterns, design patterns, debugging techniques, error handling strategies, and production best practices.
Learn essential design patterns for Node.js applications including Singleton, Factory, Observer, and Module patterns.
Content by: Praveen Kumar
MERN Stack Developer
Ensure a class has only one instance and provide global access to it.
// Factory pattern for creating objects
class DatabaseConnectionFactory {
static createConnection(type) {
switch (type) {
case 'mysql':
return new MySQLConnection();
case 'postgresql':
return new PostgreSQLConnection();
case 'mongodb':
return new MongoDBConnection();
default:
throw new Error('Unsupported database type');
}
}
}
class MySQLConnection {
connect() {
console.log('Connecting to MySQL');
}
}
class PostgreSQLConnection {
connect() {
console.log('Connecting to PostgreSQL');
}
}
// Usage
const db = DatabaseConnectionFactory.createConnection('mysql');
db.connect();// Event-driven architecture with Observer pattern
class EventEmitter {
constructor() {
this.events = {};
}
on(event, listener) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(listener);
}
emit(event, data) {
if (this.events[event]) {
this.events[event].forEach(listener => listener(data));
}
}
}
// Usage
const emitter = new EventEmitter();
emitter.on('user:created', (user) => {
console.log('User created:', user);
});
emitter.emit('user:created', { id: 1, name: 'John' });// Module pattern for encapsulation
const UserModule = (() => {
let users = [];
return {
addUser: (user) => {
users.push(user);
},
getUsers: () => [...users],
getUserById: (id) => users.find(u => u.id === id)
};
})();
// Usage
UserModule.addUser({ id: 1, name: 'John' });
console.log(UserModule.getUsers());// Strategy pattern for different algorithms
class PaymentProcessor {
constructor(strategy) {
this.strategy = strategy;
}
processPayment(amount) {
return this.strategy.process(amount);
}
}
class CreditCardStrategy {
process(amount) {
return `Processing ${amount} via Credit Card`;
}
}
class PayPalStrategy {
process(amount) {
return `Processing ${amount} via PayPal`;
}
}
// Usage
const processor = new PaymentProcessor(new CreditCardStrategy());
console.log(processor.processPayment(100));// Complete design patterns implementation
class Logger {
constructor() {
if (Logger.instance) {
return Logger.instance;
}
this.logs = [];
Logger.instance = this;
}
log(message) {
const timestamp = new Date().toISOString();
this.logs.push({ message, timestamp });
console.log(`[${timestamp}] ${message}`);
}
getLogs() {
return this.logs;
}
}
// Factory for different types of handlers
class HandlerFactory {
static createHandler(type) {
switch (type) {
case 'email':
return new EmailHandler();
case 'sms':
return new SMSHandler();
case 'push':
return new PushHandler();
default:
throw new Error('Unknown handler type');
}
}
}
class EmailHandler {
send(message) {
console.log(`Sending email: ${message}`);
}
}
class SMSHandler {
send(message) {
console.log(`Sending SMS: ${message}`);
}
}
// Observer pattern for notifications
class NotificationService {
constructor() {
this.subscribers = [];
}
subscribe(callback) {
this.subscribers.push(callback);
}
notify(data) {
this.subscribers.forEach(callback => callback(data));
}
}
// Usage
const logger = new Logger();
const notificationService = new NotificationService();
notificationService.subscribe((data) => {
logger.log(`Notification sent: ${data.message}`);
});
const emailHandler = HandlerFactory.createHandler('email');
emailHandler.send('Welcome to our service!');Test your understanding of this topic:
Master advanced debugging techniques for Node.js applications including profiling, memory debugging, and performance analysis.
Content by: Sachin Patel
Node.js Developer
Learn to use various debugging tools and techniques to identify and fix issues in Node.js applications.
// Using Node.js built-in debugger
// Start with: node --inspect app.js
// Or: node --inspect-brk app.js
const debug = require('debug')('myapp');
// Debug logging
debug('This is a debug message');
// Conditional debugging
if (process.env.DEBUG) {
console.log('Debug information:', data);
}
// Using console.trace for stack traces
function problematicFunction() {
console.trace('Function called from:');
// Your code here
}// Memory usage monitoring
function logMemoryUsage() {
const used = process.memoryUsage();
console.log('Memory Usage:');
console.log('RSS:', Math.round(used.rss / 1024 / 1024), 'MB');
console.log('Heap Total:', Math.round(used.heapTotal / 1024 / 1024), 'MB');
console.log('Heap Used:', Math.round(used.heapUsed / 1024 / 1024), 'MB');
console.log('External:', Math.round(used.external / 1024 / 1024), 'MB');
}
// Memory leak detection
const heapUsed = process.memoryUsage().heapUsed;
setInterval(() => {
const currentHeap = process.memoryUsage().heapUsed;
const diff = currentHeap - heapUsed;
if (diff > 10 * 1024 * 1024) { // 10MB
console.warn('Potential memory leak detected');
}
}, 30000);// Performance profiling
const { performance, PerformanceObserver } = require('perf_hooks');
// Measure function execution time
function measureTime(fn, label) {
const start = performance.now();
const result = fn();
const end = performance.now();
console.log(`${label} took ${end - start} milliseconds`);
return result;
}
// Performance observer
const obs = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
console.log(`${entry.name}: ${entry.duration}ms`);
});
});
obs.observe({ entryTypes: ['measure'] });
// Mark and measure
performance.mark('start');
// Your code here
performance.mark('end');
performance.measure('operation', 'start', 'end');// Enhanced error handling with stack traces
class CustomError extends Error {
constructor(message, code) {
super(message);
this.name = 'CustomError';
this.code = code;
Error.captureStackTrace(this, this.constructor);
}
}
// Stack trace analysis
function analyzeStackTrace(error) {
const stack = error.stack;
const lines = stack.split('
');
console.log('Error:', error.message);
console.log('Stack trace:');
lines.forEach((line, index) => {
if (index > 0) {
console.log(` ${index}: ${line.trim()}`);
}
});
}
// Usage
try {
throw new CustomError('Something went wrong', 'ERR_001');
} catch (error) {
analyzeStackTrace(error);
}// Complete debugging toolkit
const fs = require('fs');
const path = require('path');
class Debugger {
constructor(options = {}) {
this.logFile = options.logFile || 'debug.log';
this.level = options.level || 'info';
this.levels = ['error', 'warn', 'info', 'debug'];
}
log(level, message, data = {}) {
if (this.levels.indexOf(level) <= this.levels.indexOf(this.level)) {
const logEntry = {
timestamp: new Date().toISOString(),
level,
message,
data,
memory: process.memoryUsage(),
pid: process.pid
};
console.log(`[${level.toUpperCase()}] ${message}`, data);
// Write to file
fs.appendFileSync(this.logFile, JSON.stringify(logEntry) + '
');
}
}
error(message, data) {
this.log('error', message, data);
}
warn(message, data) {
this.log('warn', message, data);
}
info(message, data) {
this.log('info', message, data);
}
debug(message, data) {
this.log('debug', message, data);
}
// Memory monitoring
startMemoryMonitoring(interval = 30000) {
setInterval(() => {
const memUsage = process.memoryUsage();
this.debug('Memory usage', {
rss: Math.round(memUsage.rss / 1024 / 1024),
heapTotal: Math.round(memUsage.heapTotal / 1024 / 1024),
heapUsed: Math.round(memUsage.heapUsed / 1024 / 1024)
});
}, interval);
}
// Performance monitoring
time(label) {
const start = process.hrtime.bigint();
return {
end: () => {
const end = process.hrtime.bigint();
const duration = Number(end - start) / 1000000; // Convert to milliseconds
this.debug(`${label} completed`, { duration: `${duration}ms` });
return duration;
}
};
}
}
// Usage
const debugger = new Debugger({ level: 'debug' });
debugger.startMemoryMonitoring();
const timer = debugger.time('database-query');
// Simulate database query
setTimeout(() => {
timer.end();
}, 1000);Test your understanding of this topic:
Learn comprehensive error handling strategies for production Node.js applications including error classification, recovery, and monitoring.
Content by: Parth Patel
Node.js Developer
Classify errors into different categories to handle them appropriately and provide better user experience.
// Custom error classes
class AppError extends Error {
constructor(message, statusCode, isOperational = true) {
super(message);
this.statusCode = statusCode;
this.isOperational = isOperational;
this.timestamp = new Date().toISOString();
Error.captureStackTrace(this, this.constructor);
}
}
class ValidationError extends AppError {
constructor(message, field = null) {
super(message, 400);
this.field = field;
this.type = 'ValidationError';
}
}
class NotFoundError extends AppError {
constructor(resource = 'Resource') {
super(`${resource} not found`, 404);
this.type = 'NotFoundError';
}
}
class DatabaseError extends AppError {
constructor(message, originalError = null) {
super(message, 500);
this.type = 'DatabaseError';
this.originalError = originalError;
}
}// Retry mechanism with exponential backoff
class RetryHandler {
constructor(maxRetries = 3, baseDelay = 1000) {
this.maxRetries = maxRetries;
this.baseDelay = baseDelay;
}
async execute(fn, context = {}) {
let lastError;
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error;
if (attempt === this.maxRetries || !this.isRetryable(error)) {
throw error;
}
const delay = this.baseDelay * Math.pow(2, attempt - 1);
console.log(`Attempt ${attempt} failed, retrying in ${delay}ms`);
await this.sleep(delay);
}
}
throw lastError;
}
isRetryable(error) {
// Define which errors are retryable
const retryableErrors = ['ECONNRESET', 'ETIMEDOUT', 'ENOTFOUND'];
return retryableErrors.includes(error.code);
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
// Circuit breaker pattern
class CircuitBreaker {
constructor(threshold = 5, timeout = 60000) {
this.threshold = threshold;
this.timeout = timeout;
this.failureCount = 0;
this.lastFailureTime = null;
this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
}
async execute(fn) {
if (this.state === 'OPEN') {
if (Date.now() - this.lastFailureTime > this.timeout) {
this.state = 'HALF_OPEN';
} else {
throw new Error('Circuit breaker is OPEN');
}
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
onSuccess() {
this.failureCount = 0;
this.state = 'CLOSED';
}
onFailure() {
this.failureCount++;
this.lastFailureTime = Date.now();
if (this.failureCount >= this.threshold) {
this.state = 'OPEN';
}
}
}// Global error handling middleware
const errorHandler = (err, req, res, next) => {
let error = { ...err };
error.message = err.message;
// Log error
console.error('Error:', err);
// Mongoose bad ObjectId
if (err.name === 'CastError') {
const message = 'Resource not found';
error = new NotFoundError(message);
}
// Mongoose duplicate key
if (err.code === 11000) {
const message = 'Duplicate field value entered';
error = new ValidationError(message);
}
// Mongoose validation error
if (err.name === 'ValidationError') {
const message = Object.values(err.errors).map(val => val.message).join(', ');
error = new ValidationError(message);
}
// JWT errors
if (err.name === 'JsonWebTokenError') {
const message = 'Invalid token';
error = new AppError(message, 401);
}
if (err.name === 'TokenExpiredError') {
const message = 'Token expired';
error = new AppError(message, 401);
}
res.status(error.statusCode || 500).json({
success: false,
error: error.message || 'Server Error',
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
});
};
// Async error wrapper
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
// Unhandled promise rejections
process.on('unhandledRejection', (err, promise) => {
console.error('Unhandled Promise Rejection:', err);
// Close server & exit process
process.exit(1);
});
// Uncaught exceptions
process.on('uncaughtException', (err) => {
console.error('Uncaught Exception:', err);
process.exit(1);
});// Error monitoring and alerting
class ErrorMonitor {
constructor() {
this.errorCounts = new Map();
this.alertThreshold = 10;
this.timeWindow = 60000; // 1 minute
}
recordError(error) {
const key = `${error.name}:${error.message}`;
const now = Date.now();
if (!this.errorCounts.has(key)) {
this.errorCounts.set(key, []);
}
const errors = this.errorCounts.get(key);
errors.push(now);
// Remove old errors outside time window
const recentErrors = errors.filter(time => now - time < this.timeWindow);
this.errorCounts.set(key, recentErrors);
// Check if threshold exceeded
if (recentErrors.length >= this.alertThreshold) {
this.sendAlert(key, recentErrors.length);
}
}
sendAlert(errorKey, count) {
console.error(`ALERT: ${errorKey} occurred ${count} times in the last minute`);
// Send to monitoring service
}
}
// Usage
const errorMonitor = new ErrorMonitor();
// In error handler
errorMonitor.recordError(error);// Complete error handling system
const express = require('express');
const winston = require('winston');
// Error classes
class AppError extends Error {
constructor(message, statusCode, isOperational = true) {
super(message);
this.statusCode = statusCode;
this.isOperational = isOperational;
this.timestamp = new Date().toISOString();
Error.captureStackTrace(this, this.constructor);
}
}
class ValidationError extends AppError {
constructor(message, field = null) {
super(message, 400);
this.field = field;
this.type = 'ValidationError';
}
}
// Logger setup
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
// Error monitoring
class ErrorMonitor {
constructor() {
this.errorCounts = new Map();
this.alertThreshold = 5;
}
recordError(error) {
const key = error.constructor.name;
const count = this.errorCounts.get(key) || 0;
this.errorCounts.set(key, count + 1);
if (count >= this.alertThreshold) {
this.sendAlert(key, count);
}
}
sendAlert(errorType, count) {
logger.error(`ALERT: ${errorType} occurred ${count} times`);
}
}
const errorMonitor = new ErrorMonitor();
// Express app
const app = express();
// Error handling middleware
const errorHandler = (err, req, res, next) => {
let error = { ...err };
error.message = err.message;
// Log error
logger.error('Error occurred', {
error: err.message,
stack: err.stack,
url: req.url,
method: req.method
});
// Record error for monitoring
errorMonitor.recordError(err);
// Handle specific error types
if (err.name === 'ValidationError') {
error = new ValidationError('Validation failed');
}
res.status(error.statusCode || 500).json({
success: false,
error: error.message || 'Server Error',
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
});
};
// Async error wrapper
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
// Example route with error handling
app.get('/api/users/:id', asyncHandler(async (req, res, next) => {
const { id } = req.params;
if (!id || isNaN(id)) {
throw new ValidationError('Invalid user ID');
}
// Simulate database operation
const user = await simulateDatabaseOperation(id);
if (!user) {
throw new AppError('User not found', 404);
}
res.json({ success: true, data: user });
}));
// Simulate database operation
async function simulateDatabaseOperation(id) {
// Simulate random errors
if (Math.random() < 0.3) {
throw new Error('Database connection failed');
}
return { id, name: 'John Doe' };
}
// Global error handler
app.use(errorHandler);
// Unhandled promise rejections
process.on('unhandledRejection', (err, promise) => {
logger.error('Unhandled Promise Rejection:', err);
process.exit(1);
});
// Uncaught exceptions
process.on('uncaughtException', (err) => {
logger.error('Uncaught Exception:', err);
process.exit(1);
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
logger.info(`Server running on port ${PORT}`);
});
module.exports = app;Test your understanding of this topic:
Learn production best practices for Node.js applications including security, performance, scalability, and maintenance.
Content by: Bansi Patel
Node.js Developer
Implement security measures to protect your Node.js applications from common vulnerabilities and attacks.
// Input validation and sanitization
const Joi = require('joi');
const validator = require('validator');
// Validation schemas
const userSchema = Joi.object({
name: Joi.string().min(2).max(50).required(),
email: Joi.string().email().required(),
age: Joi.number().integer().min(0).max(150).optional()
});
// Sanitization middleware
const sanitizeInput = (req, res, next) => {
if (req.body) {
Object.keys(req.body).forEach(key => {
if (typeof req.body[key] === 'string') {
req.body[key] = validator.escape(req.body[key]);
}
});
}
next();
};
// Validation middleware
const validateUser = (req, res, next) => {
const { error } = userSchema.validate(req.body);
if (error) {
return res.status(400).json({
success: false,
error: 'Validation failed',
details: error.details
});
}
next();
};// Rate limiting and DDoS protection
const rateLimit = require('express-rate-limit');
const slowDown = require('express-slow-down');
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: 'Too many requests from this IP',
standardHeaders: true,
legacyHeaders: false
});
// Slow down repeated requests
const speedLimiter = slowDown({
windowMs: 15 * 60 * 1000, // 15 minutes
delayAfter: 50, // allow 50 requests per 15 minutes
delayMs: 500 // add 500ms delay per request after delayAfter
});
// DDoS protection
const ddosProtection = (req, res, next) => {
const ip = req.ip;
const userAgent = req.get('User-Agent');
// Check for suspicious patterns
if (userAgent && userAgent.includes('bot')) {
return res.status(403).json({ error: 'Bot detected' });
}
next();
};// Performance optimization techniques
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;
// Cluster setup for multi-core utilization
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`);
cluster.fork(); // Restart worker
});
} else {
// Worker process
const express = require('express');
const app = express();
// Compression middleware
const compression = require('compression');
app.use(compression());
// Caching middleware
const cache = require('memory-cache');
const cacheMiddleware = (duration) => {
return (req, res, next) => {
const key = req.originalUrl;
const cachedBody = cache.get(key);
if (cachedBody) {
return res.send(cachedBody);
} else {
res.sendResponse = res.send;
res.send = (body) => {
cache.put(key, body, duration * 1000);
res.sendResponse(body);
};
next();
}
};
};
app.use(cacheMiddleware(300)); // Cache for 5 minutes
}// Health check and monitoring
const healthCheck = {
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage(),
version: process.env.npm_package_version,
environment: process.env.NODE_ENV
};
// Health check endpoint
app.get('/health', (req, res) => {
res.json(healthCheck);
});
// Detailed health check
app.get('/health/detailed', async (req, res) => {
const health = {
...healthCheck,
database: await checkDatabaseHealth(),
redis: await checkRedisHealth(),
externalServices: await checkExternalServices()
};
res.json(health);
});
async function checkDatabaseHealth() {
try {
// Check database connection
return { status: 'healthy', responseTime: '50ms' };
} catch (error) {
return { status: 'unhealthy', error: error.message };
}
}// Complete production-ready Node.js application
const express = require('express');
const helmet = require('helmet');
const compression = require('compression');
const rateLimit = require('express-rate-limit');
const winston = require('winston');
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;
// Logger setup
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console());
}
// Cluster setup
if (cluster.isMaster) {
logger.info(`Master ${process.pid} is running`);
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
logger.error(`Worker ${worker.process.pid} died`);
cluster.fork();
});
} else {
const app = express();
// Security middleware
app.use(helmet());
app.use(compression());
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
standardHeaders: true,
legacyHeaders: false
});
app.use(limiter);
// Body parsing
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));
// Request logging
app.use((req, res, next) => {
logger.info('Request', {
method: req.method,
url: req.url,
ip: req.ip,
userAgent: req.get('User-Agent')
});
next();
});
// Routes
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage(),
pid: process.pid
});
});
app.get('/api/status', (req, res) => {
res.json({ status: 'OK', timestamp: new Date().toISOString() });
});
// Error handling
app.use((err, req, res, next) => {
logger.error('Error occurred', {
error: err.message,
stack: err.stack,
url: req.url,
method: req.method
});
res.status(500).json({
error: 'Internal Server Error',
message: process.env.NODE_ENV === 'production'
? 'Something went wrong'
: err.message
});
});
// 404 handler
app.use((req, res) => {
res.status(404).json({ error: 'Not Found' });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
logger.info(`Worker ${process.pid} started on port ${PORT}`);
});
}
// Graceful shutdown
process.on('SIGTERM', () => {
logger.info('SIGTERM received, shutting down gracefully');
process.exit(0);
});
process.on('SIGINT', () => {
logger.info('SIGINT received, shutting down gracefully');
process.exit(0);
});Test your understanding of this topic:
Continue your learning journey and master the next set of concepts.
Back to Course Overview