How a Wrong Singleton Implementation Crushed Our Redis Connections in NestJS
Source: Dev.to
The Singleton Pattern
For those who might need a refresher: the Singleton pattern ensures that a class has only one instance throughout the application’s lifecycle.
- This instance is created once when the app starts.
- All other services share this single instance instead of creating new ones, which is crucial for resource‑heavy tasks like database or message broker connections.
Dependency Injection (DI) in NestJS
Singleton behavior is tightly coupled with Dependency Injection. In NestJS, Singleton is the default scope for DI.
- During the bootstrap phase, NestJS registers these dependencies in the IoC (Inversion of Control) Container.
- To use a service, you simply inject it via the class constructor, and NestJS handles the rest.
The “Legacy” Mistake: When DI Goes Wrong
In NestJS, the @Injectable() decorator defaults to a Singleton scope. If you need a service to be available everywhere (like Config, Logger, or a shared client), you can use the @Global() decorator.
The Issue We Faced
Despite the service being marked as a Singleton, the legacy code was manually re‑declaring the service in the providers array of multiple modules and overusing the @Inject() annotation unnecessarily.
Result: Developers followed a “copy‑paste” pattern. Every time a new service needed to talk to another microservice, it inadvertently triggered a new instance of ClientProxy. This led to an explosion of active connections to Redis, creating a massive bottleneck and slowing down the entire infrastructure.
The Fix
The solution was straightforward but required a thorough cleanup:
- Centralized Module – Created a dedicated
ClientProxyModuleto house theClientProxyService. - Global Decorator – Added the
@Global()decorator to this module so it’s initialized once at the root level. - Refactoring – Removed redundant provider declarations and unnecessary
@Inject()annotations from other services. We switched back to standard constructor injection.
Pro‑Tips for Debugging
How do you know if you’ve handled Singletons correctly? Here are two quick tricks:
- The Constructor Log – Add a
console.loginside theconstructorof your service. Restart the app. If you see that log more than once, you have an instantiation leak. It should only fire once during startup. - The Getter Log – If you use a getter method, add a log there to monitor exactly when and where the service is being accessed.
Final Thoughts
Whether you are using NestJS, Spring Boot, or .NET, the core principle of the Singleton Pattern remains vital.
- Understand the Scope – Don’t just copy‑paste. Understand how your framework manages instances.
- Audit your Connections – Whether it’s a monolith or microservices, “blind injection” can lead to resource exhaustion.
Check your current projects—are you injecting services recklessly? Let’s keep our connections clean and our systems fast.
Happy coding! 🚀