AskHandle Blog
NodeJS Design Patterns That Make Code Better

NodeJS Design Patterns That Make Code Better
Design patterns help developers write better and cleaner code. As a NodeJS developer, I've found these patterns super useful in my day-to-day work. Let me share some practical patterns that will make your NodeJS applications more maintainable and scalable.
Singleton Pattern
The Singleton pattern makes sure you have only one instance of a class throughout your application. I use this pattern when I need to manage shared resources like database connections or configuration settings. Here's what it looks like in action:
1class DatabaseConnection {
2 constructor() {
3 if (DatabaseConnection.instance) {
4 return DatabaseConnection.instance;
5 }
6 this.connection = "connected";
7 DatabaseConnection.instance = this;
8 }
9}Factory Pattern
The Factory pattern creates objects without directly using the 'new' keyword. I find this pattern great for creating different types of objects based on conditions. Think of it as a smart object creator:
1class UserFactory {
2 createUser(type) {
3 switch(type) {
4 case 'admin':
5 return new AdminUser();
6 case 'guest':
7 return new GuestUser();
8 default:
9 return new RegularUser();
10 }
11 }
12}Observer Pattern
This pattern works like a subscription system. One object (subject) maintains a list of observers and notifies them of state changes. I use this pattern when building event-driven systems:
1class EventEmitter {
2 constructor() {
3 this.events = {};
4 }
5
6 on(event, callback) {
7 if (!this.events[event]) {
8 this.events[event] = [];
9 }
10 this.events[event].push(callback);
11 }
12
13 emit(event, data) {
14 if (this.events[event]) {
15 this.events[event].forEach(callback => callback(data));
16 }
17 }
18}Module Pattern
The Module pattern keeps code organized and encapsulated. NodeJS uses this pattern through its require/export system. I structure my applications using this pattern:
1// userService.js
2const userService = {
3 getUser() { /* ... */ },
4 updateUser() { /* ... */ },
5 deleteUser() { /* ... */ }
6};
7
8module.exports = userService;Middleware Pattern
Express.js popularized this pattern in NodeJS. The pattern chains different processing steps together. I use this pattern to handle requests in a clean, organized way:
1const app = express();
2app.use(auth);
3app.use(logging);
4app.use(errorHandling);Dependency Injection
This pattern makes testing easier and reduces coupling between components. Instead of creating dependencies inside a class, you pass them in:
1class UserService {
2 constructor(database, logger) {
3 this.db = database;
4 this.logger = logger;
5 }
6
7 async getUser(id) {
8 this.logger.info(`Fetching user ${id}`);
9 return this.db.users.findById(id);
10 }
11}Repository Pattern
The Repository pattern separates data access logic from business logic. I use this pattern to make database operations more manageable:
1class UserRepository {
2 async findById(id) {
3 return await db.query('SELECT * FROM users WHERE id = ?', [id]);
4 }
5
6 async save(user) {
7 return await db.query('INSERT INTO users SET ?', user);
8 }
9}These patterns make code more organized, testable, and maintainable. Start with one pattern and gradually add more as your application grows. Each pattern solves specific problems, so pick the ones that fit your needs.