La programación modular en NestJS te permite encapsular funcionalidades específicas dentro de módulos independientes, lo que hace tu aplicación más escalable y mantenible. Si vienes trabajando con el app module por defecto y tu proyecto empieza a crecer, este enfoque te ayuda a organizar controladores, servicios y entidades por dominio. Ideal para desarrolladores backend que construyen APIs con NestJS y buscan estructuras profesionales.
Qué es un módulo en NestJS y por qué importa
Un módulo es una unidad que encapsula toda la funcionalidad de un dominio o feature dentro de tu aplicación. Piensa en un e-commerce: en lugar de mezclar todo, separas la lógica en módulos independientes que conviven entre sí.
Ejemplos típicos de módulos en una aplicación real:
- Módulo de órdenes de compra con sus controllers, servicios y entidades.
- Módulo de autenticación con su propia lógica de login y validación.
- Módulo de facturación con sus reglas y endpoints específicos.
- Módulo de usuarios para gestionar perfiles y consultas.
¿Qué hace un módulo en NestJS? Agrupa controladores, servicios y entidades relacionadas con una misma funcionalidad, creando un scope aislado que puede comunicarse con otros módulos de forma controlada.
La clave está en que cada módulo tenga responsabilidad sobre un solo dominio. Así, cuando tu app crezca, sabrás exactamente dónde tocar para modificar una feature.
Cómo creo un módulo con el CLI de NestJS
NestJS trae un generador que te ahorra el trabajo manual. Desde la terminal ejecutas el comando y listo.
bash
nest g mo users
Este comando hace dos cosas: crea el archivo users.module.ts dentro de la carpeta users y actualiza automáticamente el app.module.ts para incluirlo en los imports. Es un patrón que verás repetido en cualquier proyecto productivo de NestJS.
Cómo refactorizo el App Module para usar módulos
Una vez generado el módulo, toca limpiar el app.module.ts. La idea es que el App Module solo declare otros módulos, no contenga controladores ni servicios propios.
Los pasos concretos del refactor:
- Elimina el
AppController y el AppService que vienen por defecto.
- Borra los imports asociados y el archivo de pruebas del controller.
- Mueve
UserController y UserService al UserModule, dentro de sus arrays controllers y providers.
- Deja el
AppModule solo con módulos en imports, como ConfigModule y UserModule.
Después del refactor, tu AppModule queda limpio y declarativo. Es la estructura que vas a encontrar en aplicaciones reales con módulos como PostModule, AuthenticationModule o ChatModule.
Cómo comparto un servicio entre módulos con exports
Aquí viene una de las partes más potentes de la arquitectura modular en NestJS: el control de qué se comparte y qué se mantiene privado. Por defecto, un servicio declarado en un módulo solo puede usarse dentro de ese mismo scope.
Si quieres que otro módulo, como AuthModule, pueda inyectar UserService para hacer un login por email, debes exportarlo explícitamente:
typescript
@Module({
controllers: [UserController],
providers: [UserService],
exports: [UserService],
})
export class UserModule {}
¿Por qué exportar un servicio en NestJS? Porque sin el array exports, otros módulos no pueden inyectar ese servicio. Es el mecanismo que protege los límites de tu aplicación y evita acoplamientos no deseados.
Normalmente se exportan los servicios, no los controladores. Los controllers exponen endpoints HTTP hacia el exterior, mientras que los servicios contienen la lógica reutilizable. Esa separación es la que mantiene tu código sano cuando el proyecto escala.
Qué pasa con entidades, DTOs y modelos dentro del módulo
Las entidades, Data Transfer Objects (DTOs) y modelos también viven dentro de la carpeta del módulo, pero no necesitas declararlos explícitamente en el decorador @Module(). Solo controladores y servicios se registran ahí.
Esto te permite mantener toda la estructura de archivos organizada por feature, sin saturar la configuración del módulo.
Qué hago si NestJS no detecta los cambios después del refactor
Un detalle práctico que aparece al refactorizar: a veces el servidor en modo watch se queda con una versión vieja del AppModule y lanza errores diciendo que no encuentra el AppController. La solución es simple.
- Detén el proceso en la terminal.
- Vuelve a ejecutar el comando de inicio.
- Verifica los endpoints en Postman para confirmar que todo responde.
Los endpoints viejos como /my-test o el Hello World deberían devolver un 404 not found, mientras que los de usuarios siguen funcionando igual: GET, POST, PATCH y DELETE responden sin problema en el puerto que tengas configurado, por ejemplo el 4001 si lo defines en una variable de entorno.
Con esto ya tienes tu primera aplicación NestJS estructurada con programación modular. Cuéntame en los comentarios qué módulos planeas crear primero en tu proyecto.