NestJS Tips
Tags: nestjs, typescript, tips, spanish
Published: 2021-06-30 17:12:56

---
title: NestJS Tips
published: true
description: NestJS es un framework para backend (inicialmente) que me atrevo a pensar que se volverá estándar. Hoy te comparto algunos tips aprendidos.
tags: nestjs, typescript, tips, spanish
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d4p224axahtxlfsmgmg5.png
---
[NestJS](https://nestjs.com/) es un framework para backend (inicialmente) que la está empezando a romper y me atrevo a pensar que se volverá estándar poco a poco en la industria. Basado en Express, pero dándole un upgrade importante con [TypeScript](https://www.typescriptlang.org/). Si has programado [Angular](https://angular.io/) en sus versiones recientes creo que te será bastante conocido.


<figure class="image">
  <img src="https://cdn-images-1.medium.com/max/1200/1*3Tw7--k2b-nLQs11aSDyBQ.png" alt="Screenshot State of JS">
  <figcaption>State of JS 2020: https://2020.stateofjs.com/es-ES/technologies/back-end-frameworks/</figcaption>
</figure>
---

## CLI

La CLI o Línea de comandos es una de las cosas que nos hará más simple la vida pues nos permite de forma rápida crear archivos con la estructura necesaria par el correcto funcionamiento con el framework. Es algo que actualmente los frameworks o librerías enfocadas a enterprise ven ya como parte de un mínimo para su distribución. Lo principal es el comando 

```bash
nest generate / nest g
```

Con el cual podremos crear diferentes tipos de elementos en nuestro proyecto.

### Archivos generables


|Nombre|Alias|Descripción|
|-|-|-|
|controller|co|Crear un controlador simple en la ruta que le demos|
|provider|pr|Crear un proveedor o servicio|
|pipe|pi|Genera el código para un nuevo pipe|
|gateway|ga|Podemos crear un gateway para el uso de [Socket.io](https://socket.io/)|
|guard|gu|Como en [Angular](https://angular.io/) los Guards son para la seguridad y esto nos dará lo básico en el nuevo|

Existen muchos más comandos. Si deseas ver la lista completa ingresa a: [https://docs.nestjs.com/cli/usages](https://docs.nestjs.com/cli/usages)

---

## Configuración

Algo importante al desarrollar software con altos estándares es no "quemar" datos en código tales como la dirección de la base de datos, el usuario, etc. Para esto existen varias prácticas y en este caso lo hacemos a través de un archivo **.env.**

Para esto lo primero que haremos es instalar un par de dependencias en nuestro proyecto 

```bash
npm i --save @nestjs/config
```

Una vez instalada esta dependencia empezaremos creando un archivo **.env** en la raíz de nuestro proyecto que se verá algo así

```bash
PORT=3000
CORS='true'
LOGGER='true'
DB_HOST='xxx.xxx.xxx.xxxx'
DB_PORT='xxxx'
DB_USER='xxxxxxxx'
DB_PASSWORD='xxxxxxxxxxx'
DB_DATABASE='xxxxxxxxx'
```

Ahora que tenemos este archivo vamos a agregar uno en **./src** con el nombre **config.ts**. La idea de este segundo archivo es convertir esta data del archivo **.env** en un [JSON](https://www.json.org/) que sea fácilmente legible en el futuro desde cualquier parte de la aplicación.

```tsx
export const config = () => ({
  port: Number(process.env.PORT),
  cors: process.env.CORS === 'true',
  logger: process.env.LOGGER === 'true',
  database: {
    type: 'mysql',
    host: process.env.DB_HOST,
    port: Number(process.env.DB_PORT),
    username: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    database: process.env.DB_DATABASE,
	},
});
```

Ahora que hemos preparado los recursos necesarios podemos integrar la configuración a nuestro proyecto.

Iremos a nuestro **./src/app.module.ts** y en la sección  de **imports** agregaremos el módulo de configuración de la siguiente manera.

```tsx
...
import { ConfigModule } from '@nestjs/config';
import { config } from './config'
....
@Module({
	imports: [
		...
		ConfigModule.forRoot({
			isGlobal: true,
			load: [config]
		}),
		...
	],Ï
	...
})
export class AppModule {}
```

Una vez que agregamos esta instrucción la configuración que es cargada por el **ConfigModule** es convertida en [JSON](https://www.json.org/) y puesta a disponibilidad de toda la aplicación.

Sin embargo necesitamos algunos de estos valores antes de iniciar la aplicación en nuestro archivo ./main.ts, por lo que haremos lo siguiente:

```tsx
import { Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const configService = app.get(ConfigService);
  const cors = configService.get('CORS') === 'true';
  if (cors) {
    app.enableCors();
  }
  const logger = configService.get('LOGGER') === 'true';
  if (logger) {
    app.useLogger(new Logger());
  }
  await app.listen(configService.get('PORT'));
  console.log(`Application is running on: ${await app.getUrl()}`);
}
bootstrap();
```

En el caso de que utilices TypeORM como yo, te dejo este pequeño truco: Para activar su configuración con este mismo archivo **.env.** Lo primero que debes hacer es agregar un archivo en el mismo **./src** el cual llamaremos **database.config.ts** y agregaremos lo siguiente:

```tsx
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { TypeOrmOptionsFactory } from '@nestjs/typeorm';

@Injectable()
export class DatabaseConfig implements TypeOrmOptionsFactory {
  constructor(private configService: ConfigService) {}

  createTypeOrmOptions() {
    const config = this.configService.get('database');
    return config;
  }
}
```

De esta manera el archivo tomará el servicio que nos da el **ConfigModule** y podremos leer la configuración desde el [JSON](https://www.json.org/) que designamos. Incluso para hacerlo más eficiente vemos que se obtiene solo la sección de **database**, ya que en este caso es lo único que nos interesa.

Ahora en nuestro archivo de **./src/app.module.ts** vamos a modificar nuestra carga del módulo:

```tsx
...
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { config } from './config';
import { DatabaseConfig } from './database.config';
....
@Module({
	imports: [
		...
		ConfigModule.forRoot({
			isGlobal: true,
			load: [config]
		}),
		TypeOrmModule.forRootAsync({
			imports: [ConfigModule],
			useClass: DatabaseConfig
		}),
		...
	],Ï
	...
})
export class AppModule {}  ****
```

Es importante notar que primero debe cargar el **ConfigModule** antes del **TypeOrmModule**.

---

## Logger

En las aplicaciones de servidor es muy importante tener un tracking continuo de las acciones de la misma para que, de ser necesario, encontremos el error o la irregularidad cuanto antes.

Para esto podremos aprovechar el módulo de configuración que vimos anteriormente y agregaremos cosas como los niveles de registros que queremos utilizar sin tener que modificar el código de la aplicación.

Lo primero para esto es agregar una línea a nuestro archivo **.env**:

```bash
...
LOGGER_LEVELS='error,warn,log,verbose,debug'
...
```

Estos son los 5 niveles que nos dará el framework y los he ordenado según su prioridad, al menos en mi opinión. A continuación agregaremos estos datos a nuestro [JSON](https://www.json.org/) en **./src/config.ts**:

```tsx
export const config = () => ({
	...
	loggerLevels: process.env.LOGGER_LEVELS.split(',') || [], 
	...
});
```

Ya hemos preparado el terreno, pero continuaremos creando nuestro propio **provider** el cual podrá ser importado por los otros **providers** y **controllers** fácilmente. Para esto usaremos nuestro CLI con el comando:

```bash
nest g pr providers/mylogger/mylogger.provider
```

El cual nos dará un archivo que para cuando terminemos de modificarlo debería verse algo así:

```tsx
import { Injectable, Logger, Scope } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
@Injectable({ scope: Scope.TRANSIENT })
export class MyLoggerProvider extends Logger {
  levels: string[] = ['error', 'warn', 'log', 'verbose', 'debug'];
  constructor(private config: ConfigService) {
    super();
    this.levels = this.config.get('loggerLevels');
  }

  log(message: string, context?: string) {
    if (this.levels.includes('log')) {
      super.log(message, context);
    }
  }
  error(message: string, trace?: string, context?: string) {
    if (this.levels.includes('error')) {
      super.error(message, trace, context);
    }
  }
  warn(message: string, context?: string) {
    if (this.levels.includes('warn')) {
      super.warn(message, context);
    }
  }
  debug(message: string, context?: string) {
    if (this.levels.includes('debug')) {
      super.debug(message, context);
    }
  }
  verbose(message: string, context?: string) {
    if (this.levels.includes('verbose')) {
      super.verbose(message, context);
    }
  }
}
```

Como podemos ver lo primero es que hemos cambiado el tipo de **Scope** para la **Inyección** del **Provider** por una transitoria, lo cual permite que cada cliente de este **Provider** obtenga una instancia independiente. Esto lo queremos así pues nos permitirá manejar el contexto del cliente y saber quien escribió cada registro.

Después observamos que este **provider** deberá extender de la clase nativa de [NestJS](https://nestjs.com/) **Logger** la cual nos dará acceso a los métodos de cada tipo de registro.

Finalmente en el constructor de la clase vemos como obtenemos el servicio o **provider** del **ConfigModule** con el cual podemos leer la información que nos interesa. Esta información es validada en cada tipo de registro para saber si debe o no ejecutarse la función.

Como último paso iremos a nuestro archivo **./src/main.ts** y haremos un pequeño cambio para configurar nuestro nuevo **Logger**:

```tsx
import { MyLoggerProvider } from 'providers/mylogger/mylogger.provider';
import { ConfigService } from '@nestjs/config';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
	...
  if (logger) {
    app.useLogger(new MyLoggerProvider(configService));
  }
	...
}
bootstrap();
```

---

## [Swagger](https://swagger.io/)

Esta es una herramienta que ha ganado mucho uso en la documentación de las aplicaciones de Backend debido a lo simple que es poder visualizar y probar los diferentes endpoints de una API.

En [NestJS](https://nestjs.com/) esto se puede agregar fácilmente con unos pequeños ajustes. 

Primero vamos a instalar algunas dependencias.

```bash
npm i --save @nestjs/swagger swagger-ui-express
```

Una vez instalado esto procedemos a nuestro archivo **./main.ts** para habilitarlo:

```tsx
import { MyLoggerProvider } from 'providers/mylogger/mylogger.provider';
import { ConfigService } from '@nestjs/config';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
	...
  const config = new DocumentBuilder()
	  .setTitle('My new API')
	  .setDescription('Swagger for my new API')
	  .setVersion('1.0')
	  .addBearerAuth(
			{ type: 'http', scheme: 'bearer', bearerFormat: 'JWT' },
			  'XYZ',
	  )
	  .build();
	  const document = SwaggerModule.createDocument(app, config);
	  SwaggerModule.setup('swagger', app, document);
	...
}
bootstrap();
```

Esto levantará un servicio de [Swagger](https://swagger.io/) en **/swagger** para que puedas probar tus endpoints de una forma amigable. Es importante destacar que por ejemplo, aquí estamos agregando la autenticación de [JWT](https://jwt.io/) al [Swagger](https://swagger.io/) para que puedas usar las APIs privadas.

En los controladores que se autentican con [JWT](https://jwt.io/), tendremos que agregar un decorador que puede ir en el controlador o bien en cada una de las funciones por lo que se verá algo así:

```tsx
// Controllers
@ApiBearerAuth('XYZ')
@UseGuards(JwtAuthGuard)
@Controller('settings')

// Funciones
@ApiBearerAuth('XYZ')
@UseGuards(JwtAuthGuard)
@Get()
```

---

## Documentación

Sé que hacer la documentación de un proyecto es algo que a la mayoría de desarrolladores no nos encanta. Justamente por eso creo que está herramienta es de mis favoritas. En un simple comando tendremos una documentación bastante rigurosa, entendible e incluso interactiva del proyecto gracias al generador [compodoc](https://compodoc.app/). Para ello solo escribiremos lo siguiente en la raíz de nuestro proyecto.

```bash
npx @compodoc/compodoc -p tsconfig.json -s
```

Esto genera una carpeta **documentation** en la raíz del proyecto y levanta un servicio [HTTP](https://es.wikipedia.org/wiki/Protocolo_de_transferencia_de_hipertexto) para que podamos verlo en http://localhost:8080 :

![Screenshot compodoc](https://cdn-images-1.medium.com/max/800/1*FMV0CcnY-xaYpjkl33EEtQ.png)

Yo sugiero guardar este comando como parte de nuestros scripts en el archivo **package.json**

---

Bueno creo que con estos son bastantes tips y herramientas que podemos empezar a integrar en nuestros proyectos con este framework. Si bien tengo algunos más creo que los dejaré para un próximo post.

Priméro quisiera saber que les han parecido los que hemos visto y que me digan si quieren que investigue específicamente alguna funcionalidad.