The Singleton Pattern: Why You Only Need One (and How to Use It)
The Singleton pattern guarantees that only one instance of a class exists throughout an application. It acts as a centralized access point for shared resources like databases, loggers, and configuration managers. Simple in concept yet powerful in practice, Singleton plays a critical role in system architecture — but when misused, it can introduce global state problems and testing challenges.
Published on 8 feb 2026

Table of Contents
1. What is the Singleton Pattern?
In software engineering, a Singleton is a design pattern that ensures a class has only one instance and provides a global point of access to it.
2. How It Works?
To implement a Singleton, you typically follow three specific rules:
- Private Constructor: You prevent other classes from using the new keyword by making the constructor private.
- Static Variable: You create a private static variable within the class that holds the single instance.
- Public Static Method: You provide a public method (often named getInstance()) that returns the instance. If the instance doesn't exist yet, it creates it; otherwise, it returns the existing one.
3. Why Use It?
The Singleton pattern is mostly used for managing shared resources. Common real-world examples include:
-
Database Connections: You don't want to open 100 separate connections to a database if one shared connection can handle the load.
-
Logging: A single logger instance usually handles all messages from different parts of an application to keep the logs in chronological order.
-
Configuration Settings: You only need one object to hold the global "dark mode" or "language" settings for an entire app.
4. While useful, the Singleton is sometimes called an "anti-pattern" if overused because:
- Global State: It can make debugging difficult because any part of your code can change the object’s state.
- Testing: It’s harder to write unit tests because the Singleton persists state between tests.
- Tight Coupling: It makes classes dependent on a specific global instance, reducing flexibility
5. How is a Singleton implemented in JavaScript?
class SettingsConfig { constructor() { if (SettingsConfig.instance) { return SettingsConfig.instance; } this.theme = 'dark'; this.apiEndpoint = 'https://api.example.com'; // Save the instance on the class itself SettingsConfig.instance = this; return this; } } // Usage: const instanceA = new SettingsConfig(); const instanceB = new SettingsConfig(); console.log(instanceA === instanceB); // true
5.1 Using "Static" and Private Fields (Modern)
class Database { static #instance; constructor() { if (Database.#instance) { throw new Error('Use Database.getInstance() instead of new.'); } this.connectionString = 'postgres://localhost:5432'; } static getInstance() { if (!Database.#instance) { Database.#instance = new Database(); } return Database.#instance; } } const db1 = Database.getInstance(); // const db2 = new Database(); // This would throw an error!
In TypeScript, implementing a Singleton is straightforward because of its support for private constructors and static members. The goal is to ensure that even if you try to create a new instance, the class politely points you back to the original one
class DatabaseConnection { // 1. Create a private static instance of the class private static instance: DatabaseConnection; // 2. Make the constructor private so no one can call 'new DatabaseConnection()' private constructor() { console.log('Connected to the Database!'); } // 3. Provide a static method to get the instance public static getInstance(): DatabaseConnection { if (!DatabaseConnection.instance) { DatabaseConnection.instance = new DatabaseConnection(); } return DatabaseConnection.instance; } public query(sql: string) { console.log(`Executing: ${sql}`); } } // Usage: const connection1 = DatabaseConnection.getInstance(); const connection2 = DatabaseConnection.getInstance(); console.log(connection1 === connection2); // true (It's the exact same object)
5.2 Cleaner Version (Recommended Pattern in Node.js)
// logger.ts class Logger { log(msg: string) { console.log(msg); } } // Export a single instance export const logger = new Logger();
Because of how ES Modules work, logger.ts is only executed once. Every time you import logger in other files, you are getting the same instance. This avoids the complexity of private constructors while achieving the same result.
In short, the Singleton Pattern is your go-to solution when you need to ensure that a class has only one instance and that this instance is easily accessible from anywhere in your app.

