Domina los Injection Scopes en NestJS: DEFAULT, REQUEST y TRANSIENT
- nestjs
- typescript
- backend
- di
Una de las características más poderosas de NestJS es su sistema de inyección de dependencias, que permite controlar cómo se crean y comparten las instancias de los servicios (providers) dentro de tu aplicación. En este artículo exploraremos los tres tipos de scopes que NestJS ofrece: DEFAULT, REQUEST y TRANSIENT.
🔍 Los Tres Tipos de Scope en NestJS
NestJS permite definir el ciclo de vida de cada provider a través de su scope. Esto afecta cómo se instancia y reutiliza un servicio dentro de tu aplicación:
- DEFAULT (Singleton): Se crea una sola instancia compartida por toda la aplicación, su ciclo de vida está ligado al de la aplicación.
- REQUEST: Se crea una nueva instancia por cada solicitud HTTP.
- TRANSIENT: Se crea una nueva instancia cada vez que se inyecta el servicio.
🧪 Aplicación de Ejemplo
Vamos a construir una aplicación con varios servicios, cada uno utilizando un scope diferente. Cada servicio tendrá un contador interno que incrementa cada vez que se llama su método increase().
Desde el controlador, inyectamos los cuatro servicios y exponemos un endpoint GET que los ejecuta. Así podremos observar cómo cambia el comportamiento del contador en cada scope.
https://github.com/aitormunoz/nest-injection-scope 🔗
1. Scope DEFAULT (DefaultService)
Este servicio se crea una sola vez al iniciar la aplicación y se reutiliza en todas las solicitudes. Es el scope por defecto en NestJS, y también el más común. Esto se debe a que ofrece el mejor rendimiento, ya que evita crear múltiples instancias.
👉 Ideal para servicios que mantienen estado compartido o recursos globales.
Resultado:
Request 1: { default: 1, ... }
Request 2: { default: 2, ... }
Request 3: { default: 3, ... }
Cada llamada al endpoint incrementa el contador, la instancia es la misma durante toda la vida útil de la aplicación.
2. Scope REQUEST (RequestService)
Se crea una nueva instancia para cada solicitud. Por lo tanto, el contador siempre empieza desde cero.
👉 Útil cuando necesitas mantener estado aislado por cada request, como el contexto de usuario.
Resultado:
Request 1: { request: 1, ... }
Request 2: { request: 1, ... }
Request 3: { request: 1, ... }
3. Scope TRANSIENT (TransientService)
Cada vez que se inyecta, se genera una nueva instancia, incluso dentro de la misma solicitud.
👉 Perfecto para servicios que requieren una instancia limpia por cada uso.
Resultado:
Request 1: { transient: 1, ... }
Request 2: { transient: 1, ... }
Request 3: { transient: 1, ... }
Cada llamada al endpoint devuelve 1, porque el contador interno siempre empieza desde cero. Pero espera… ¿No dijimos que TRANSIENT crea nuevas instancias cada vez que se inyecta? Entonces, ¿por qué no vemos un incremento como con los demás servicios?
Lo que estás viendo aquí es un fenómeno conocido como scope bubbling 🔗. Es un comportamiento muy importante a tener en cuenta cuando usas scopes dinámicos.
Y aquí está la clave:
Como el controlador depende de RequestService, se convierte automáticamente también en request-scoped. Esto es el bubbling: el scope de RequestService “sube” a AppController.
✅ DEFAULT / Singleton
- Una sola instancia compartida.
- Uso recomendado: configuraciones globales, conexiones a base de datos, cachés.
🔁 REQUEST
- Una instancia por solicitud HTTP.
- Ideal para: contexto del usuario, logs por request, estado aislado.
🌀 TRANSIENT
- Una instancia por cada inyección.
- Uso típico: servicios sin estado, factory pattern, generación de datos únicos.
🧭 Cuándo Usar Cada Scope
🧩 Conclusión Final
Comprender cómo funcionan los scopes de NestJS te permite diseñar aplicaciones más eficientes y predecibles. Elegir el scope adecuado para cada servicio te ayuda a evitar errores sutiles relacionados con el estado compartido y a optimizar el rendimiento de tu app.