Inyección de Dependencias
Usa el contenedor DI de NestJS 11 para ámbitos, proveedores personalizados y cableado de servicios comprobable.
Receta
Tarjeta de receta de referencia rápida: lista para copiar y pegar.
import { Module, Injectable, Inject } from "@nestjs/common";
@Injectable()
export class UsersRepository {
findById(id: string) { return { id, name: "Ada" }; }
}
@Injectable()
export class UsersService {
constructor(private readonly repo: UsersRepository) {}
getUser(id: string) {
return this.repo.findById(id);
}
}
@Module({
providers: [UsersRepository, UsersService],
exports: [UsersService],
})
export class UsersModule {}Cuándo usarlo: Cualquier servicio de NestJS que dependa de otro servicio, repositorio o cliente externo.
Ejemplo de trabajo
import { Module, Injectable, Inject } from "@nestjs/common";
import { Test } from "@nestjs/testing";
// Token de interfaz para implementaciones intercambiables
export const EMAIL_SENDER = Symbol("EMAIL_SENDER");
export interface EmailSender {
send(to: string, subject: string): Promise<void>;
}
@Injectable()
export class SmtpEmailSender implements EmailSender {
async send(to: string, subject: string) {
console.log(`Enviando a ${to}: ${subject}`);
}
}
@Injectable()
export class UsersService {
constructor(
@Inject(EMAIL_SENDER) private readonly email: EmailSender,
) {}
async register(email: string) {
await this.email.send(email, "¡Bienvenido!");
return { email };
}
}
@Module({
providers: [
UsersService,
{ provide: EMAIL_SENDER, useClass: SmtpEmailSender },
],
})
export class UsersModule {}
// Anulación de prueba
const testModule = await Test.createTestingModule({
imports: [UsersModule],
})
.overrideProvider(EMAIL_SENDER)
.useValue({ send: async () => {} })
.compile();Lo que esto demuestra:
- Inyección de servicios en el constructor
- Tokens de proveedor personalizados con
Symbol useClasspara el enlace de interfaz a implementaciónoverrideProviderpara dobles de prueba
Análisis en profundidad
Cómo funciona
- NestJS crea un contenedor DI al iniciar
@Injectable()registra una clase como proveedor- Los tipos de parámetros del constructor determinan qué inyectar
- El contenedor resuelve el grafo de dependencias antes de atender las solicitudes
Ámbitos del proveedor
| Ámbito | Vida útil | Usar para |
|---|---|---|
DEFAULT (singleton) | Una instancia por aplicación | Servicios, repositorios |
REQUEST | Una por solicitud HTTP | Contexto de usuario con ámbito de solicitud |
TRANSIENT | Nueva instancia por inyección | Ayudantes sin estado (raro) |
@Injectable({ scope: Scope.REQUEST })
export class RequestContextService {
userId?: string;
}Patrones de proveedor personalizados
| Patrón | Sintaxis | Usar para |
|---|---|---|
useClass | { provide: TOKEN, useClass: Impl } | Enlace de interfaz |
useValue | { provide: TOKEN, useValue: obj } | Configuración, mocks |
useFactory | { provide: TOKEN, useFactory: fn, inject: [...] } | Configuración asíncrona |
useExisting | { provide: TOKEN, useExisting: OtherToken } | Alias |
Errores comunes
- Dependencias circulares - A inyecta B que inyecta A. Solución:
forwardRef(() => B)o refactorizar la lógica compartida a un tercer servicio. - Ámbito de solicitud en singleton - inyectar un servicio de solicitud en un singleton falla. Solución: usar
ModuleRefo hacer que el consumidor también tenga ámbito de solicitud. - Olvidar
exports- el servicio no está disponible en el módulo importador. Solución: añadir al arrayexports. new Service()manual - omite la DI y la capacidad de prueba. Solución: inyectar siempre a través del constructor.- Demasiados proveedores en un módulo - antipatrón de módulo "Dios". Solución: dividir en módulos de características.
- No anular en las pruebas - las pruebas acceden a la base de datos real. Solución:
overrideProviderouseValuemock.
Alternativas
| Alternativa | Usar cuándo | No usar cuándo |
|---|---|---|
| DI manual (parámetros del constructor) | Aplicación simple de Fastify/Express | Proyecto NestJS (usar DI incorporada) |
| tsyringe | DI sin el framework NestJS | Ya estás en NestJS |
| Funciones de fábrica | Servicios ligeros | Grafos de dependencia complejos |
| ModuleRef.get() | Resolución dinámica de proveedores | La inyección normal del constructor funciona |
Preguntas frecuentes
¿Cómo inyecto el ConfigService?
Importa ConfigModule.forRoot({ isGlobal: true }) e inyecta ConfigService en cualquier proveedor.
¿Puedo inyectar datos de solicitud en un servicio?
Usa @Inject(REQUEST) con un proveedor con ámbito de solicitud, o pasa los datos del controlador al método del servicio (más simple).
¿Cómo comparto un proveedor entre módulos?
Añádelo a providers y exports en el módulo proveedor. Importa ese módulo donde sea necesario.
¿Qué es forwardRef?
Retrasa la resolución de una dependencia circular. Úsalo con moderación; la refactorización suele ser mejor.
¿Cómo inyecto una conexión a la base de datos?
useFactory con limpieza onModuleDestroy, o usa el wrapper @nestjs/typeorm / PrismaService.
¿La DI añade sobrecarga de rendimiento?
Mínima en tiempo de ejecución (resolución de singleton una vez). El ámbito de solicitud añade un costo de instanciación por solicitud.
¿Cómo pruebo un controlador con un servicio simulado?
Test.createTestingModule({ controllers: [X], providers: [{ provide: Y, useValue: mock }] }).compile().
¿Puedo usar DI sin decoradores?
No idiomáticamente en NestJS. Los decoradores son el mecanismo central del framework.
Relacionado
- Conceptos básicos de NestJS - estructura del módulo
- Guards, Interceptors y Pipes - inyección transversal
- NestJS + Prisma/TypeORM - DI de la capa de datos
- Patrones de inyección de dependencias - DI agnóstica al framework
- Mejores prácticas de NestJS - lista de verificación de la sección
Versiones de la pila: Esta página fue escrita para Node.js 24.18.0 (LTS activa), npm 10+, TypeScript 5.6+, Express 5, Fastify 5 y NestJS 11.