Design Patterns in Node.js
Learn essential design patterns for Node.js applications including Singleton, Factory, Observer, and Module patterns.
90 minā¢By Priygop Teamā¢Last updated: Feb 2026
Singleton Pattern
Ensure a class has only one instance and provide global access to it.
Factory Pattern
Example
// 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();Observer Pattern
Example
// 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
Example
// 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
Example
// 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));Mini-Project: Complete Pattern Implementation
Example
// 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!');