Nest.js - Patrón arquitectónico, controladores, proveedores y módulos

¿Por qué podríamos necesitar Nest.js cuando tenemos Node.js?

· 5 min de lectura
Nest.js - Patrón arquitectónico, controladores, proveedores y módulos

Nest (NestJS) es un marco de trabajo para construir aplicaciones eficientes y escalables del lado del servidor de Node.js. Utiliza JavaScript progresivo, y está construido con TypeScript y lo soporta completamente. - Documentos de NestJS

Esencialmente, NestJS es una capa sobre Node que tiene métodos e implementaciones sobrecargadas que pueden ayudarnos a escribir aplicaciones del lado del servidor de forma rápida y sencilla.

Nest es muy conveniente para contener todas tus necesidades. ¡Es altamente personalizable y por defecto utiliza Express bajo el capó, pero puede ser configurado opcionalmente para utilizar Fastify también!

⚠️⚠️ATENCIÓN: Tenemos curso de NestJS

via GIPHY

¿Por qué podríamos necesitar Nest.js cuando tenemos Node.js?

  • Nest proporciona una capa de abstracción sobre Node, lo que significa que ahora puede aprovechar las características de Node así como exponer APIs sobrealimentadas para un mejor rendimiento y eficiencia.
  • A los desarrolladores les encantan las funciones y cuando sabes que tienes más de ellas, simplemente no puedes negarlo. Ese es el caso de Nest.js. Ahora tienes acceso a toneladas de módulos de terceros que pueden acelerar tu proceso de desarrollo.
  • Creo que no es en absoluto una desviación completa del paradigma del backend. Seguimos escribiendo el mismo tipo de código, una estructura muy similar pero con una capa añadida de robustez.
  • NestJS también es altamente configurable con ORM's (como TypeORM) que podemos aprovechar para trabajar con bases de datos. Esto significa de nuevo que también tienes grandes características de TypeORM como el patrón Active Record y Data Mapper que ahora puedes aprovechar fácilmente.
El patrón Active Record necesariamente puede ayudarte a obtener simplicidad mientras que el patrón Data Mapper puede ayudar a que tu código sea más mantenible.
  • Otro punto a añadir es que Nest admite que su arquitectura está fuertemente inspirada en Angular. Siempre es una buena idea disponer de pruebas sin esfuerzo cuando se necesitan y una forma de mantener la base de código de manera eficiente. Y, Nest te proporciona precisamente eso. ¡Una estructura muy necesaria!

Arquitectura de niveles a la que Nest.js está predispuesto.


Cuando hablamos de establecer una arquitectura firme, lo que más nos importa es cómo aislamos las diferentes partes de una aplicación de forma que la parte que tiene sentido en conjunto conviva. Seguir un patrón arquitectónico puede supuestamente ayudar a resolver el código espagueti que puedas estar escribiendo.

Veamos un ejemplo

via GIPHY

Considerando el diagrama de flujo de abajo, nos damos cuenta de que los controladores y la capa de servicio trabajan juntos para llevar a cabo una lógica, pero son dos cosas completamente diferentes.

Los controladores se ocupan esencialmente de las rutas de su aplicación. Un controlador puede tener varias rutas diferentes y todo depende del mecanismo de enrutamiento para controlar qué controlador recibe qué solicitud.

Pero qué pasa si vuelcas toda tu lógica de negocio dentro de los controladores. Tal vez registrar un nuevo usuario si el usuario aún no está registrado o llevar a cabo controles de validación sólo dentro de los controladores.

Eso no tiene mucho sentido después de todo. Probablemente no haría mucha diferencia si sólo tienes una pequeña aplicación. Pero cuando la aplicación crece y tienes que registrar más controladores y rutas y tienes que escribir más lógica de negocio, ahí es donde las cosas parecen salirse de control y ciertamente no son mantenibles.

  1. Controladores: El único propósito de un controlador es recibir las peticiones de la aplicación y ocuparse de las rutas.
  2. Capa de servicio: Esta parte del bloque debe incluir únicamente la lógica de negocio. Por ejemplo, todas las operaciones CRUD y los métodos para determinar cómo se pueden crear, almacenar y actualizar los datos.
  3. Capa de acceso a los datos: Esta capa se encarga y proporciona la lógica para acceder a los datos almacenados en un almacenamiento persistente de algún tipo. Por ejemplo un ODM como Mongoose.

Estructura del directorio del proyecto NestJS.


Una vez que andamiajes un nuevo proyecto NestJS utilizando tu CLI, que le da unos pocos archivos de la plantilla para empezar. Estos son los archivos principales con los que normalmente se trabaja. La estructura de directorios sería algo así:

src
| — app.controller.spec.ts
| — app.controller.ts
| — app.module.ts
| — app.service.ts
| — main.ts
  1. app.controller.ts: Archivo del controlador que contendrá todas las rutas de la aplicación.
  2. app.controller.spec.ts:  Este archivo ayudaría a escribir pruebas unitarias para los controladores.
  3. app.module.ts El archivo de módulo esencialmente agrupa todos los controladores y proveedores de su aplicación.
  4. app.service.ts El servicio incluirá métodos que realizarán una determinada operación. Por ejemplo: Registrar un nuevo usuario.
  5. main.ts El archivo de entrada de la aplicación tomará su paquete de módulos y creará una instancia de la aplicación utilizando el NestFactory proporcionado por Nest.
Enseguida sabes que hay una cierta estructura establecida que debes seguir. Y eso es lo que ayuda a los desarrolladores a escribir un código limpio, escalable y mantenible.

Veamos algo de código.


Creemos un archivo de servicio y exportemos una clase llamada CatsService que implementará algunos métodos que podemos importar y utilizar en nuestro archivo controlador.

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

@Injectable()
export class CatsService {
  getAllCats(): string {
    return 'Get all cats!';
  }

  getOneCat(): string {
    return 'Get one cat!';
  }
}

Aquí estamos exportando una clase llamada CatsService y hemos definido tres métodos diferentes. También utilizamos @Injectable() que es un decorador.

El decorador adjunta metadatos y marca una clase disponible para ser proporcionada e inyectada como dependencia. Ya que hemos inyectado esta clase como dependencia, vamos a utilizarla dentro del controlador para recuperar los datos.

import { Controller, Get } from '@nestjs/common';
import { CatsService } from '../services/cats.service';

@Controller('cats')
export class CatsController {
  constructor(private readonly catsService: CatsService) {}

  @Get('allcats')
  getCats(): string {
    return this.catsService.getAllCats();
  }

  @Get('onecat')
  getOneCat(): string {
    return this.catsService.getOneCat();
  }
}

En el archivo del controlador, estamos importando el CatsService y hemos instanciado el servicio dentro del constructor.

La dependencia del CatsService se inyecta a través del constructor de la clase (Inyección de Dependencia). Esto expondrá todos los métodos que hemos definido dentro del CatsService. Ahora podemos recuperar los gatos utilizando el controlador.

Las dependencias son servicios u objetos que una clase necesita para realizar su función. La inyección de dependencias, o DI, es un patrón de diseño en el que una clase solicita dependencias de fuentes externas en lugar de crearlas. Angular

Aquí el decorador @Get() toma un argumento (el método que se está decorando, en este caso, getCats y getOneCat) y devuelve la misma función con funcionalidad añadida (o un valor de retorno en este caso, que es una cadena).

Además, el decorador @Get() acepta un nombre de ruta que podemos utilizar para solicitar una ruta concreta. Por ejemplo, una petición a /cats/allCats invocará el método getCats y devolverá todos los cats.

Haciendo una petición al endpoint

Vamos a realizar una petición al endpoint getAllCats que acabamos de definir y ver qué nos devuelve.

Como podemos ver el resultado de retorno se recibe como se esperaba. Este es técnicamente el patrón que vamos a ver mucho con Nest.

Tenemos un archivo de servicio que implementará todos los métodos/lógicos de nuestra aplicación mientras que el archivo controlador se encargará de devolver los resultados de los servicios utilizando las rutas adecuadas.

import { Module } from '@nestjs/common';
import { CatsController } from './controllers/cats.controller';
import { CatsService } from './services/cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
})
export class CatsModule {}

El archivo principal arranca una aplicación Nest para nosotros mientras se crea el módulo (CatsModule) que acabamos de crear. El archivo principal es el punto de entrada de nuestra aplicación. Para crear una instancia de aplicación Nest, Nest utiliza la clase central NestFactory.

NestFactory expone métodos estáticos que permiten crear una instancia de aplicación.

Ahí lo tenemos. Tenemos una aplicación simple de NestJS que describe más o menos las mejores prácticas y patrones que sigue NestJS y cómo se puede ayudar a construir código escalable, mantenible y comprobable.

Esperamos que esta introducción a NestJS haya sido bastante sencilla y pueda ayudarte a entender los bloques y piezas que funcionan juntos dentro de NestJS.

Referencias:

Plataforma de cursos gratis sobre programación

Artículos Relacionados

Primero pasos en Qwik
· 22 min de lectura
No todo es Hidratación Qwik
· 8 min de lectura