AskHandle Blog

NodeJS Design Patterns That Make Code Better

December 24, 2025Katherine Holland3 min read

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:

javascript
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:

javascript
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:

javascript
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:

javascript
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:

javascript
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:

javascript
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:

javascript
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.