Providers

Intermedio
Nest
Nest
Actualizado: 05/05/2026

Diagrama: tutorial-nest-providers

Qué es un Provider en Nest

Un Provider en NestJS es un concepto fundamental que representa cualquier clase que puede ser inyectada como dependencia en otras partes de la aplicación. Los providers encapsulan la lógica de negocio y proporcionan servicios específicos que pueden ser reutilizados a lo largo de toda la aplicación.

flowchart TD
    Module[Module - providers array] --> Container[Contenedor de DI]
    Container --> UserService[UserService]
    Container --> MailService[MailService]
    Container --> ConfigToken[APP_CONFIG - useValue]
    UserController[UserController] -. constructor .-> UserService
    UserService -. constructor .-> MailService
    UserService -. Inject APP_CONFIG .-> ConfigToken

El diagrama muestra el flujo de inyección de dependencias: el módulo declara sus providers, el contenedor de NestJS los instancia y los inyecta automáticamente en los constructores de quien los solicite. El UserController recibe UserService sin llamar a new, y UserService puede a su vez declarar otras dependencias que el contenedor resuelve en cascada.

En términos simples, un provider es una clase decorada con @Injectable() que puede ser inyectada en constructores de otras clases. Esta característica permite crear aplicaciones modulares y mantenibles, donde cada componente tiene responsabilidades bien definidas.

Características principales de los Providers

Los providers en NestJS tienen varias características que los hacen especiales:

  • Inyección automática: NestJS se encarga de crear e inyectar automáticamente las instancias de los providers donde sean necesarios
  • Singleton por defecto: Cada provider se crea una sola vez y se reutiliza en toda la aplicación
  • Gestión del ciclo de vida: El framework maneja la creación y destrucción de los providers
  • Tipado fuerte: Aprovechan el sistema de tipos de TypeScript para mayor seguridad
import { Injectable } from '@nestjs/common';

@Injectable()
export class CalculadoraService {
  sumar(a: number, b: number): number {
    return a + b;
  }

  multiplicar(a: number, b: number): number {
    return a * b;
  }
}

Tipos de Providers

NestJS reconoce diferentes tipos de providers según su propósito y funcionalidad:

  • 1. Servicios: Contienen lógica de negocio y operaciones específicas del dominio
@Injectable()
export class UsuarioService {
  private usuarios = [];

  obtenerTodos() {
    return this.usuarios;
  }

  crear(usuario: any) {
    this.usuarios.push(usuario);
    return usuario;
  }
}
  • 2. Repositorios: Manejan el acceso y manipulación de datos
@Injectable()
export class ProductoRepository {
  private productos = new Map();

  guardar(id: string, producto: any) {
    this.productos.set(id, producto);
  }

  buscarPorId(id: string) {
    return this.productos.get(id);
  }
}
  • 3. Factories: Crean instancias de otros objetos o configuraciones
@Injectable()
export class ConfiguracionFactory {
  crearConfiguracion(entorno: string) {
    return {
      baseDatos: entorno === 'produccion' ? 'prod-db' : 'dev-db',
      puerto: entorno === 'produccion' ? 3000 : 3001
    };
  }
}

El decorador @Injectable()

El decorador @Injectable() es obligatorio para que una clase pueda funcionar como provider. Este decorador le dice a NestJS que la clase puede ser gestionada por el sistema de inyección de dependencias.

// Mal: sin @Injectable() no funcionará como provider
export class EmailService {
  enviarEmail(destinatario: string, mensaje: string) {
    console.log(`Enviando email a ${destinatario}: ${mensaje}`);
  }
}

// Bien: con @Injectable() funciona correctamente como provider
@Injectable()
export class EmailService {
  enviarEmail(destinatario: string, mensaje: string) {
    console.log(`Enviando email a ${destinatario}: ${mensaje}`);
  }
}

Registro de Providers

Para que un provider esté disponible en la aplicación, debe ser registrado en un módulo. Esto se hace en el array providers del decorador @Module():

import { Module } from '@nestjs/common';
import { UsuarioService } from './usuario.service';
import { EmailService } from './email.service';

@Module({
  providers: [
    UsuarioService,
    EmailService
  ],
  // Otros elementos del módulo...
})
export class UsuarioModule {}

Ámbito de los Providers

Los providers en NestJS tienen diferentes ámbitos que determinan cómo se crean y comparten las instancias:

  • Singleton (por defecto): Una sola instancia compartida en toda la aplicación
  • Request: Una nueva instancia para cada petición HTTP
  • Transient: Una nueva instancia cada vez que se inyecta
import { Injectable, Scope } from '@nestjs/common';

// Provider singleton (comportamiento por defecto)
@Injectable()
export class ContadorService {
  private contador = 0;
  
  incrementar() {
    return ++this.contador;
  }
}

// Provider con ámbito de petición
@Injectable({ scope: Scope.REQUEST })
export class SesionService {
  private datosTemporales = {};
  
  guardarDato(clave: string, valor: any) {
    this.datosTemporales[clave] = valor;
  }
}

La comprensión de los providers es esencial para aprovechar al máximo el sistema de inyección de dependencias de NestJS, ya que forman la base sobre la cual se construye toda la arquitectura modular del framework.

Custom providers: useValue, useFactory, useClass, useExisting

Más allá de las clases con @Injectable(), NestJS soporta cuatro formas de declarar un provider en el array providers. Cada una resuelve un escenario distinto:

@Module({
  providers: [
    // 1. Standard: NestJS instancia la clase
    UserService,

    // 2. useValue: registra un valor literal o un mock para tests
    { provide: 'APP_CONFIG', useValue: { apiUrl: 'https://api.example.com', timeout: 5000 } },

    // 3. useFactory: ejecuta una función para construir el provider en tiempo de arranque
    {
      provide: 'DATABASE_CONNECTION',
      useFactory: async (config: ConfigService) => {
        const conn = await createConnection({
          host: config.get('DB_HOST'),
          port: config.get('DB_PORT'),
        });
        return conn;
      },
      inject: [ConfigService],
    },

    // 4. useClass: usa una clase distinta según el entorno
    {
      provide: PaymentGateway,
      useClass: process.env.NODE_ENV === 'production' ? StripeGateway : MockGateway,
    },

    // 5. useExisting: alias de un provider ya registrado
    { provide: 'LOGGER_SERVICE', useExisting: AppLoggerService },
  ],
})
export class AppModule {}

useFactory es la forma estándar de lazy initialization: la conexión a base de datos no se crea hasta que el módulo se inicializa, y inject declara las dependencias que la factory necesita. Es el patrón que usan internamente módulos como TypeOrmModule.forRootAsync o ConfigModule.forRoot.

Inyección por token con @Inject()

Cuando el provider se registra con un token de tipo string o Symbol (no por clase), el consumidor lo inyecta con @Inject(TOKEN):

@Injectable()
export class ApiClient {
  constructor(
    @Inject('APP_CONFIG') private readonly config: AppConfig,
    @Inject('DATABASE_CONNECTION') private readonly db: Connection,
  ) {}
}

Esta variante es esencial cuando inyectas valores primitivos, configuraciones, conexiones de terceros (Redis, Kafka, S3) o múltiples implementaciones de la misma interfaz registradas con tokens distintos. Para evitar errores de tipado conviene declarar los tokens como export const APP_CONFIG = Symbol('APP_CONFIG'); y crear interfaces que tipen el provider.

Providers exportados entre módulos

Un provider declarado en providers solo es accesible desde dentro del módulo. Si otro módulo lo necesita, debe exportarse y el módulo consumidor debe importarlo:

@Module({
  providers: [UserService, UserRepository],
  exports: [UserService], // UserRepository queda privado al módulo
})
export class UsersModule {}

@Module({
  imports: [UsersModule],
  providers: [OrdersService], // OrdersService puede inyectar UserService
})
export class OrdersModule {}

Este encapsulamiento es lo que permite escalar la aplicación: cada módulo expone solo su API pública (servicios) y oculta sus detalles internos (repositorios, helpers). Romper este principio (exportar todo "por si acaso") elimina la modularidad real y facilita ciclos de dependencia.

Testing con mocks de providers

El sistema de DI hace trivial sustituir un provider real por un mock en tests unitarios:

const moduleRef = await Test.createTestingModule({
  providers: [
    OrdersService,
    {
      provide: UserService,
      useValue: {
        findById: jest.fn().mockResolvedValue({ id: '1', email: 'test@test.com' }),
      },
    },
    {
      provide: getRepositoryToken(Order),
      useClass: Repository, // o un mock más específico
    },
  ],
}).compile();

const service = moduleRef.get<OrdersService>(OrdersService);

Test.createTestingModule reproduce el contenedor de DI con las sustituciones declaradas. Es el patrón estándar para tests unitarios y de integración con Jest.

Fuentes y referencias

Documentación oficial y recursos externos para profundizar en Nest

Documentación oficial de Nest
Alan Sastre - Autor del tutorial

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 un provider y su función en NestJS. Identificar los diferentes tipos de providers: servicios, repositorios y factories. Entender el uso del decorador @Injectable() para definir providers. Aprender a registrar providers en módulos. Conocer los distintos ámbitos de los providers y su impacto en la creación de instancias.