Design Patterns in Node.js
Learn common design patterns and their implementation in Node.js applications including Singleton, Factory, Observer, and Middleware patterns
80 min•By Priygop Team•Last updated: Feb 2026
Why Design Patterns Matter
Design patterns are reusable solutions to commonly occurring problems in software design. In Node.js, patterns help manage asynchronous complexity, improve code organization, and make applications more maintainable and scalable.
Key Design Patterns
- Singleton: Ensures a class has only one instance (e.g., DB connection)
- Factory: Creates objects without specifying exact class
- Observer: Event-driven pub/sub communication
- Middleware: Chain of responsibility for request processing
- strategy: Interchangeable algorithms at runtime
- Decorator: Add behavior to objects dynamically
Singleton Pattern
Example
// Singleton Pattern - Database Connection
class DatabaseConnection {
constructor() {
if (DatabaseConnection.instance) {
return DatabaseConnection.instance;
}
this.connection = null;
this.isConnected = false;
DatabaseConnection.instance = this;
}
async connect(uri) {
if (this.isConnected) {
return this.connection;
}
try {
// Simulate connection
this.connection = { uri, connected: true };
this.isConnected = true;
console.log('Database connected successfully');
return this.connection;
} catch (error) {
console.error('Connection failed:', error);
throw error;
}
}
disconnect() {
this.isConnected = false;
this.connection = null;
console.log('Database disconnected');
}
}
// Usage - always returns same instance
const db1 = new DatabaseConnection();
const db2 = new DatabaseConnection();
console.log(db1 === db2); // true - same instance!
await db1.connect('mongodb://localhost:27017/mydb');
// db2 also has the connection nowFactory & Observer Patterns
Example
// Factory Pattern
class LoggerFactory {
static create(type, options = {}) {
switch (type) {
case 'console':
return new ConsoleLogger(options);
case 'file':
return new FileLogger(options);
case 'database':
return new DatabaseLogger(options);
default:
throw new Error(`Unknown logger type: ${type}`);
}
}
}
// Observer Pattern (EventEmitter)
const EventEmitter = require('events');
class OrderSystem extends EventEmitter {
constructor() {
super();
this.orders = [];
}
placeOrder(order) {
this.orders.push(order);
this.emit('orderPlaced', order);
this.emit('inventoryUpdate', order.items);
}
cancelOrder(orderId) {
this.orders = this.orders.filter(o => o.id !== orderId);
this.emit('orderCancelled', orderId);
}
}
const orderSystem = new OrderSystem();
// Subscribe to events
orderSystem.on('orderPlaced', (order) => {
console.log('Email sent for order:', order.id);
});
orderSystem.on('orderPlaced', (order) => {
console.log('Analytics tracked:', order.id);
});
orderSystem.on('inventoryUpdate', (items) => {
console.log('Inventory updated for:', items);
});
// Trigger events
orderSystem.placeOrder({ id: 'ORD-001', items: ['laptop', 'mouse'] });Middleware Pattern
Example
// Custom Middleware Pipeline
class Pipeline {
constructor() {
this.middlewares = [];
}
use(middleware) {
this.middlewares.push(middleware);
return this; // Enable chaining
}
async execute(context) {
let index = 0;
const next = async () => {
if (index < this.middlewares.length) {
const middleware = this.middlewares[index++];
await middleware(context, next);
}
};
await next();
return context;
}
}
// Usage
const pipeline = new Pipeline();
pipeline
.use(async (ctx, next) => {
console.log('1. Authentication check');
ctx.user = { id: 1, name: 'John' };
await next();
})
.use(async (ctx, next) => {
console.log('2. Rate limiting check');
ctx.rateLimit = { remaining: 99 };
await next();
})
.use(async (ctx, next) => {
console.log('3. Request logging');
ctx.logged = true;
await next();
})
.use(async (ctx, next) => {
console.log('4. Business logic');
ctx.result = { data: 'Response data' };
await next();
});
const result = await pipeline.execute({ path: '/api/users' });
console.log('Final context:', result);Try It Yourself — Design Patterns in Node.js
Try It Yourself — Design Patterns in Node.jsJavaScript
JavaScript Editor
✓ ValidTab = 2 spaces
JavaScript|33 lines|985 chars|✓ Valid syntax
UTF-8