Referencias con ModuleRef
La clase ModuleRef proporciona una forma de acceder dinámicamente a los proveedores registrados en el contenedor de inyección de dependencias de NestJS. Esta funcionalidad resulta especialmente útil cuando necesitas obtener referencias a servicios de manera programática, sin conocer de antemano qué proveedor específico necesitarás.
ModuleRef actúa como un registro interno que mantiene referencias a todos los proveedores disponibles en el contexto actual del módulo. A diferencia de la inyección de dependencias tradicional, donde declaras explícitamente las dependencias en el constructor, ModuleRef te permite resolver dependencias en tiempo de ejecución.
Inyección básica de ModuleRef
Para utilizar ModuleRef en tu servicio, simplemente inyéctalo a través del constructor como cualquier otra dependencia:
import { Injectable } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
@Injectable()
export class DynamicService {
constructor(private moduleRef: ModuleRef) {}
async processData() {
// Aquí podremos usar moduleRef para obtener proveedores
}
}
El ModuleRef se inyecta automáticamente desde el paquete @nestjs/core
y no requiere configuración adicional en el módulo.
Obtención de proveedores con get()
El método get() es la forma principal de obtener referencias a proveedores registrados. Acepta el token del proveedor (generalmente la clase del servicio) y devuelve la instancia correspondiente:
@Injectable()
export class UserProcessor {
constructor(private moduleRef: ModuleRef) {}
async processUser(userId: string) {
// Obtener el servicio de usuarios dinámicamente
const userService = this.moduleRef.get(UserService);
// Usar el servicio obtenido
const user = await userService.findById(userId);
return user;
}
}
También puedes usar tokens personalizados si has registrado proveedores con identificadores específicos:
// En el módulo
@Module({
providers: [
{
provide: 'DATABASE_CONNECTION',
useValue: createDatabaseConnection(),
},
],
})
export class AppModule {}
// En el servicio
@Injectable()
export class DataService {
constructor(private moduleRef: ModuleRef) {}
getConnection() {
// Obtener proveedor por token personalizado
const connection = this.moduleRef.get('DATABASE_CONNECTION');
return connection;
}
}
Resolución estricta y opcional
Por defecto, el método get() utiliza resolución estricta, lo que significa que lanzará una excepción si el proveedor no existe. Puedes modificar este comportamiento usando el segundo parámetro:
@Injectable()
export class FlexibleService {
constructor(private moduleRef: ModuleRef) {}
async tryGetService() {
// Resolución estricta (comportamiento por defecto)
try {
const service = this.moduleRef.get(OptionalService);
return service;
} catch (error) {
console.log('Servicio no encontrado');
}
// Resolución no estricta
const service = this.moduleRef.get(OptionalService, { strict: false });
if (service) {
return service.process();
}
return 'Servicio no disponible';
}
}
Casos de uso prácticos
ModuleRef resulta especialmente útil en escenarios donde necesitas flexibilidad dinámica. Un caso común es la implementación de factorías que crean diferentes tipos de procesadores según el contexto:
@Injectable()
export class ProcessorFactory {
constructor(private moduleRef: ModuleRef) {}
createProcessor(type: string) {
switch (type) {
case 'email':
return this.moduleRef.get(EmailProcessor);
case 'sms':
return this.moduleRef.get(SmsProcessor);
case 'push':
return this.moduleRef.get(PushProcessor);
default:
throw new Error(`Processor type ${type} not supported`);
}
}
async processNotification(type: string, data: any) {
const processor = this.createProcessor(type);
return await processor.process(data);
}
}
Otro escenario útil es la resolución condicional de servicios basada en configuración o estado de la aplicación:
@Injectable()
export class PaymentService {
constructor(
private moduleRef: ModuleRef,
private configService: ConfigService,
) {}
async processPayment(amount: number) {
const environment = this.configService.get('NODE_ENV');
// Usar diferentes procesadores según el entorno
const processorToken = environment === 'production'
? 'STRIPE_PROCESSOR'
: 'MOCK_PROCESSOR';
const processor = this.moduleRef.get(processorToken);
return await processor.charge(amount);
}
}
Limitaciones y consideraciones
Es importante entender que ModuleRef solo puede acceder a proveedores que estén disponibles en el contexto actual del módulo. Esto incluye proveedores definidos en el mismo módulo y aquellos importados desde otros módulos.
Si intentas acceder a un proveedor que no está disponible en el contexto actual, recibirás un error de resolución. En estos casos, asegúrate de que el módulo correspondiente esté correctamente importado:
@Module({
imports: [UserModule], // Necesario para acceder a UserService
providers: [ProcessorFactory],
})
export class ProcessorModule {}
La resolución dinámica también tiene implicaciones en el rendimiento, ya que se realiza en tiempo de ejecución en lugar de tiempo de compilación. Úsala cuando realmente necesites flexibilidad dinámica, no como reemplazo de la inyección de dependencias tradicional.
Fuentes y referencias
Documentación oficial y recursos externos para profundizar en Nest
Documentación oficial de Nest
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Nest es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de Nest
Explora más contenido relacionado con Nest y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
- Comprender qué es ModuleRef y su función en NestJS.
- Aprender a inyectar y utilizar ModuleRef para obtener proveedores dinámicamente.
- Conocer el método get() para resolver proveedores por token o clase.
- Entender la resolución estricta y opcional de proveedores con ModuleRef.
- Identificar casos prácticos y limitaciones del uso de ModuleRef en aplicaciones NestJS.