Singleton
Design pattern ensuring a class has only one global instance with a single point of access.
Updated on January 10, 2026
The Singleton is a creational design pattern that ensures a class has only one instance throughout the application lifecycle while providing a global access point to that instance. This pattern is used to control access to shared resources such as database connections, configuration managers, or system services.
Singleton Pattern Fundamentals
- Private constructor preventing direct class instantiation
- Private static variable storing the single class instance
- Public static method (getInstance) always returning the same instance
- Lazy or eager initialization depending on requirements
Benefits of Singleton
- Guarantees strict control over access to a single shared resource
- Saves memory by avoiding creation of multiple instances
- Provides a simplified global access point to the instance
- Enables lazy initialization to optimize performance
- Facilitates coordination between different application components
Practical Example in TypeScript
class DatabaseConnection {
private static instance: DatabaseConnection;
private connectionString: string;
private isConnected: boolean = false;
// Private constructor to prevent direct instantiation
private constructor() {
this.connectionString = process.env.DB_URL || '';
}
// Public method to get the unique instance
public static getInstance(): DatabaseConnection {
if (!DatabaseConnection.instance) {
DatabaseConnection.instance = new DatabaseConnection();
}
return DatabaseConnection.instance;
}
public connect(): void {
if (!this.isConnected) {
console.log(`Connecting to: ${this.connectionString}`);
this.isConnected = true;
}
}
public query(sql: string): void {
if (this.isConnected) {
console.log(`Executing: ${sql}`);
} else {
throw new Error('Database not connected');
}
}
}
// Usage
const db1 = DatabaseConnection.getInstance();
const db2 = DatabaseConnection.getInstance();
console.log(db1 === db2); // true - same instance
db1.connect();
db1.query('SELECT * FROM users');This example illustrates a database connection implemented as a Singleton, ensuring only one connection is created and shared throughout the application, avoiding expensive multiple connections.
Pattern Implementation
- Declare the class constructor as private to block the new operator
- Create a private static property to store the unique instance
- Implement a static getInstance() method that creates the instance if needed
- Handle synchronization in multi-threaded environments if applicable
- Consider using ES6 modules for a simpler approach in modern JavaScript
Beware of Anti-Patterns
Singleton is often considered an anti-pattern due to its global nature that complicates unit testing, creates hidden dependencies, and violates the single responsibility principle. Prefer dependency injection and IoC containers to manage shared instances in modern applications.
Alternatives and Use Cases
In modern applications, ES6 modules in JavaScript/TypeScript naturally act as singletons without requiring complex implementation. Dependency injection frameworks like NestJS, Angular, or Spring offer 'singleton' scope mechanisms that are more testable and maintainable.
// Modern approach with ES6 module
export class ConfigService {
private config: Record<string, any>;
constructor() {
this.config = {
apiUrl: process.env.API_URL,
timeout: 5000
};
}
get(key: string): any {
return this.config[key];
}
}
// Export a single instance
export const configService = new ConfigService();
// Usage in other files
import { configService } from './config';
console.log(configService.get('apiUrl'));Associated Tools and Frameworks
- InversifyJS - Dependency injection container for TypeScript with singleton scope support
- TSyringe - Lightweight dependency injection for TypeScript with decorators
- NestJS - Node.js framework with dependency injection system and native singleton providers
- Awilix - Performant IoC container for Node.js supporting different lifecycle scopes
Pro Tip
For scalable and testable applications, replace classic Singletons with dependency injection. Use IoC containers to manage object lifecycle with singleton scope while keeping your code decoupled and easily testable with mocks.
While Singleton is a recognized classic pattern, its use in modern architectures must be thoughtful. It remains relevant for specific cases (resource managers, caches, loggers) but should be implemented through dependency injection mechanisms to preserve code testability and long-term maintainability.
