NestJS

Nest

Tutorial Nest: Crear y utilizar módulos

Nest módulos: creación y uso. Aprende a crear y usar módulos en Nest con ejemplos prácticos y detallados.

Aprende Nest y certifícate

Una de las principales características de NestJS es su estructura modular. En NestJS, los módulos son un componente fundamental para organizar el código de manera efectiva y estructurada, permitiendo una clara separación de responsabilidades y facilitando el desarrollo de aplicaciones escalables.

Módulos

Un módulo es una clase decorada con el decorador @Module(). Su propósito es agrupar componentes relacionados, como proveedores (servicios), controladores, y otros módulos, lo que permite una mejor organización y separación de responsabilidades en tu aplicación.

Los módulos también ayudan a promover el principio de responsabilidad única y facilitan la reutilización y el testing de los componentes.

Cada aplicación tiene al menos un módulo, el módulo raíz, pero por lo general hay múltiples módulos, especialmente en aplicaciones más grandes.

Estructura básica de un módulo

Aquí hay una estructura básica de cómo se ve un módulo en NestJS:

import { Module } from '@nestjs/common';
import { MiServicio } from './mi-servicio.service';
import { MiControlador } from './mi-controlador.controller';

@Module({
  imports: [],
  controllers: [MiControlador],
  providers: [MiServicio],
  exports: [],
})
export class MiModulo {}

En este ejemplo:

  • Se importa el decorador @Module desde '@nestjs/common'.
  • controllers: Es un array que contiene los controladores a instanciar para este módulo. NestJS crea instancias de estos controladores y maneja las rutas definidas en ellos.
  • providers: Es un array de proveedores (servicios, repositorios, etc.) que serán instanciados y gestionados por el inyector de dependencias de NestJS dentro de este módulo.
    • Importante: Los controladores definidos en un módulo pueden inyectar directamente los proveedores listados en el mismo módulo, sin que estos proveedores necesiten ser exportados explícitamente para uso interno.
  • imports: Es un array que permite importar otros módulos. De esta manera, los proveedores y componentes que son exportados por los módulos importados pueden ser utilizados en el módulo actual.
  • exports: Es un array de proveedores (o incluso módulos) que deberían ser exportados y disponibles para otros módulos que importen este módulo. Si un servicio se declara en providers pero no en exports, solo será accesible dentro de este módulo.

Ejemplo de un módulo con imports y exports

import { Module } from '@nestjs/common';
import { OtroServicio } from './otro-servicio.service';
import { OtroModulo } from './otro-modulo.module'; // Suponemos que OtroModulo ya existe

@Module({
  imports: [OtroModulo],      // MiModulo importa OtroModulo
  providers: [OtroServicio],
  exports: [OtroServicio],    // OtroServicio es un proveedor y se exporta
})
export class MiModulo {}

En este ejemplo, MiModulo importa OtroModulo, lo que significa que puede acceder a cualquier proveedor que OtroModulo haya exportado. Además, OtroServicio es un proveedor interno de MiModulo y también se exporta, lo que lo hace accesible para cualquier otro módulo que a su vez importe MiModulo.

Módulo raíz

En cada aplicación NestJS, el módulo raíz es responsable de organizar y coordinar todos los demás módulos en la aplicación.

Por defecto, este módulo se llama AppModule.

Cuando se crea una aplicación NestJS utilizando el CLI, un AppModule básico se genera automáticamente.

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

A medida que la aplicación crezca, se pueden importar y organizar diferentes módulos en el módulo raíz para mantener una estructura limpia y modular.

Módulos Compartidos (SharedModule o CommonModule)

Un patrón muy común en NestJS es la creación de módulos compartidos. Estos módulos se utilizan para agrupar y exportar proveedores (servicios, pipes, guards, etc.) que son utilizados en múltiples módulos de la aplicación. Esto ayuda a:

  • Evitar la duplicación de código.
  • Centralizar la gestión de dependencias comunes.
  • Simplificar las importaciones en otros módulos.

Ejemplo de un módulo compartido:

// common/common.module.ts
import { Module } from '@nestjs/common';
import { UtilService } from './util.service'; // Un servicio de utilidad común

@Module({
  providers: [UtilService],
  exports: [UtilService], // Se exporta para que otros módulos lo utilicen
})
export class CommonModule {}

Ahora, cualquier módulo que necesite UtilService simplemente importará CommonModule:

// En otro módulo, por ejemplo, feature/feature.module.ts
import { Module } from '@nestjs/common';
import { CommonModule } from '../common/common.module'; // Importa el módulo compartido
import { FeatureController } from './feature.controller';
import { FeatureService } from './feature.service';

@Module({
  imports: [CommonModule], // Ahora UtilService y cualquier otro exportado por CommonModule están disponibles
  controllers: [FeatureController],
  providers: [FeatureService],
})
export class FeatureModule {}

Beneficios de la modularidad

  • Desacoplamiento: La modularidad permite que los diferentes aspectos de una aplicación estén separados en módulos independientes. Esto significa que los cambios en un módulo tienen menos probabilidades de afectar a otros módulos, lo que facilita la mantenibilidad.
  • Reusabilidad: Los módulos pueden ser diseñados para ser reutilizados en diferentes partes de la aplicación o incluso en diferentes aplicaciones, promoviendo el principio DRY (Don't Repeat Yourself).
  • Testeabilidad: Dado que los módulos están desacoplados, se facilita la escritura de pruebas unitarias para cada módulo individualmente.
  • Organización: Los módulos ayudan a estructurar el código de la aplicación de manera lógica, lo que facilita la navegación y comprensión del código.

Relación entre módulos

Dentro de una aplicación NestJS, es común tener múltiples módulos que trabajan juntos para formar la funcionalidad completa de la aplicación.

Esta relación entre módulos puede ser visualizada como un gráfico, donde los nodos son módulos y las aristas representan las relaciones de dependencia entre ellos.

En otras palabras, cada módulo puede depender de uno o más módulos para acceder a sus recursos y funcionalidades.

Creación de módulos relacionados

Veamos un ejemplo práctico de cómo se relacionan los módulos en una aplicación.

Supongamos que se está creando una aplicación de gestión de tareas.

Podría haber un módulo para gestionar usuarios, otro para tareas y tal vez uno más para autenticación.

Módulo de usuarios

// users/users.module.ts
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';

@Module({
  controllers: [UsersController],
  providers: [UsersService],
  exports: [UsersService], // Exportamos UsersService para que otros módulos puedan inyectarlo
})
export class UsersModule {}

Módulo de tareas

Para gestionar las tareas, podría ser necesario interactuar con el módulo de usuarios. En este caso, el módulo de tareas podría importar el módulo de usuarios.

// tasks/tasks.module.ts
import { Module } from '@nestjs/common';
import { TasksController } from './tasks.controller';
import { TasksService } from './tasks.service';
import { UsersModule } from '../users/users.module'; // Importa UsersModule

@Module({
  imports: [UsersModule], // Ahora TasksService puede inyectar UsersService
  controllers: [TasksController],
  providers: [TasksService],
})
export class TasksModule {}

Módulo de autenticación

Similarmente, el módulo de autenticación podría necesitar acceso al módulo de usuarios para verificar credenciales.

// auth/auth.module.ts
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { UsersModule } from '../users/users.module'; // Importa UsersModule

@Module({
  imports: [UsersModule], // Ahora AuthService puede inyectar UsersService
  providers: [AuthService],
})
export class AuthModule {}

Recomendaciones

  • Evitar dependencias circulares: Es importante garantizar que no haya dependencias circulares entre módulos, ya que esto puede causar problemas al iniciar la aplicación.
  • Minimizar las dependencias: Cuantas menos dependencias tenga un módulo, más fácil será reutilizarlo y testearlo. Se recomienda minimizar las relaciones entre módulos cuando sea posible.
  • Responsabilidad única: Cada módulo debe tener una responsabilidad única y claramente definida. Esto facilita la comprensión y mantenimiento del código.

Conclusión

Los módulos en NestJS proporcionan una forma de organizar y encapsular componentes relacionados en una aplicación. Ayudan a estructurar la aplicación en bloques lógicos y reutilizables, promoviendo buenas prácticas y facilitando la gestión y escalabilidad del código. Utilizar módulos adecuadamente en NestJS permite construir aplicaciones más limpias, más fáciles de mantener y de testear.

Aprende Nest online

Otras lecciones de Nest

Accede a todas las lecciones de Nest y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Accede GRATIS a Nest y certifícate

Ejercicios de programación de Nest

Evalúa tus conocimientos de esta lección Crear y utilizar módulos con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

En esta lección

Objetivos de aprendizaje de esta lección

  • Comprender la estructura modular en NestJS.
  • Aprender a importar y exportar módulos.
  • Conocer las características de los módulos.
  • Comprender la relación entre módulos.