Nest: Servicios e inyección de dependencias

Aprende los fundamentos de servicios e inyección de dependencias en NestJS para crear aplicaciones modulares y mantenibles con TypeScript.

Aprende Nest GRATIS y certifícate

Servicios e inyección de dependencias en Nest

Los servicios constituyen el núcleo de la arquitectura de aplicaciones en Nest, proporcionando una capa de abstracción donde reside la lógica de negocio. Estos componentes encapsulan funcionalidades específicas y pueden ser reutilizados a través de toda la aplicación mediante el sistema de inyección de dependencias.

Fundamentos de los servicios

Un servicio en Nest es una clase TypeScript decorada con @Injectable() que encapsula lógica de negocio, acceso a datos o cualquier funcionalidad que pueda ser compartida entre diferentes partes de la aplicación. Esta separación de responsabilidades permite mantener los controladores ligeros y enfocados únicamente en manejar las peticiones HTTP.

import { Injectable } from '@nestjs/common';

@Injectable()
export class UsuarioService {
  private usuarios = [
    { id: 1, nombre: 'Ana', email: 'ana@ejemplo.com' },
    { id: 2, nombre: 'Carlos', email: 'carlos@ejemplo.com' }
  ];

  obtenerTodos() {
    return this.usuarios;
  }

  obtenerPorId(id: number) {
    return this.usuarios.find(usuario => usuario.id === id);
  }
}

El decorador @Injectable() marca la clase como un proveedor que puede ser gestionado por el contenedor de inyección de dependencias de Nest. Este mecanismo permite que el framework se encargue automáticamente de crear instancias y resolver las dependencias entre componentes.

Sistema de inyección de dependencias

La inyección de dependencias es un patrón de diseño que Nest implementa de forma nativa, permitiendo que las clases declaren sus dependencias sin necesidad de crearlas manualmente. El framework se encarga de resolver y proporcionar estas dependencias automáticamente.

import { Controller, Get, Param } from '@nestjs/common';
import { UsuarioService } from './usuario.service';

@Controller('usuarios')
export class UsuarioController {
  constructor(private readonly usuarioService: UsuarioService) {}

  @Get()
  obtenerUsuarios() {
    return this.usuarioService.obtenerTodos();
  }

  @Get(':id')
  obtenerUsuario(@Param('id') id: string) {
    return this.usuarioService.obtenerPorId(+id);
  }
}

En este ejemplo, el UsuarioController declara su dependencia del UsuarioService a través del constructor. Nest automáticamente inyecta una instancia del servicio cuando crea el controlador, eliminando la necesidad de gestionar manualmente la creación de objetos.

Registro de proveedores

Para que un servicio esté disponible para la inyección de dependencias, debe ser registrado como proveedor en un módulo. Los proveedores son componentes que pueden ser inyectados como dependencias en otros componentes.

import { Module } from '@nestjs/common';
import { UsuarioController } from './usuario.controller';
import { UsuarioService } from './usuario.service';

@Module({
  controllers: [UsuarioController],
  providers: [UsuarioService],
  exports: [UsuarioService] // Opcional: exportar para uso en otros módulos
})
export class UsuarioModule {}

El array providers define qué servicios estarán disponibles dentro del módulo. La propiedad exports permite que otros módulos puedan importar y utilizar estos servicios, facilitando la modularidad de la aplicación.

Ámbitos de los proveedores

Nest maneja diferentes ámbitos para los proveedores, determinando cómo y cuándo se crean las instancias:

Singleton (por defecto):

@Injectable()
export class ConfiguracionService {
  private configuracion = {
    apiUrl: 'https://api.ejemplo.com',
    timeout: 5000
  };

  obtenerConfiguracion() {
    return this.configuracion;
  }
}

Request-scoped:

import { Injectable, Scope } from '@nestjs/common';

@Injectable({ scope: Scope.REQUEST })
export class LoggerService {
  private logs: string[] = [];

  log(mensaje: string) {
    this.logs.push(`${new Date().toISOString()}: ${mensaje}`);
  }

  obtenerLogs() {
    return this.logs;
  }
}

Los proveedores singleton mantienen una única instancia durante toda la vida de la aplicación, mientras que los request-scoped crean una nueva instancia para cada petición HTTP, útil para mantener contexto específico de cada solicitud.

Proveedores personalizados

Nest permite definir proveedores personalizados para casos más complejos donde se necesita mayor control sobre la creación de instancias:

Factory providers:

const databaseProvider = {
  provide: 'DATABASE_CONNECTION',
  useFactory: async (configService: ConfiguracionService) => {
    const config = configService.obtenerConfiguracion();
    return crearConexionBaseDatos(config.databaseUrl);
  },
  inject: [ConfiguracionService],
};

@Module({
  providers: [ConfiguracionService, databaseProvider],
  exports: ['DATABASE_CONNECTION'],
})
export class DatabaseModule {}

Value providers:

const constantesProvider = {
  provide: 'CONSTANTES_APP',
  useValue: {
    VERSION: '1.0.0',
    MAX_INTENTOS: 3,
    TIMEOUT_DEFAULT: 30000
  },
};

Los factory providers ejecutan una función para crear la instancia del proveedor, permitiendo lógica compleja de inicialización. Los value providers proporcionan valores constantes que pueden ser inyectados en otros componentes.

Inyección de dependencias avanzada

Para casos donde se necesita inyección opcional o inyección circular, Nest proporciona decoradores especializados:

import { Injectable, Optional, Inject } from '@nestjs/common';

@Injectable()
export class NotificacionService {
  constructor(
    @Optional() private emailService?: EmailService,
    @Inject('CONFIGURACION_NOTIFICACION') private config?: any
  ) {}

  enviarNotificacion(mensaje: string) {
    if (this.emailService) {
      this.emailService.enviar(mensaje);
    } else {
      console.log('Servicio de email no disponible:', mensaje);
    }
  }
}

El decorador @Optional() permite que una dependencia sea opcional, evitando errores si el proveedor no está disponible. @Inject() permite especificar tokens personalizados para la inyección, útil cuando se trabaja con proveedores que no son clases.

La arquitectura de servicios e inyección de dependencias en Nest facilita la creación de aplicaciones modulares, testeable y mantenibles, donde cada componente tiene responsabilidades bien definidas y las dependencias se gestionan de forma transparente.

Empezar curso de Nest

Lecciones de este módulo de Nest

Lecciones de programación del módulo Servicios e inyección de dependencias del curso de Nest.

Ejercicios de programación en este módulo de Nest

Evalúa tus conocimientos en Servicios e inyección de dependencias con ejercicios de programación Servicios e inyección de dependencias de tipo Test, Puzzle, Código y Proyecto con VSCode.