Hola a todos, en este post quiero hacer una actualización de mi post anterior en Nest.js. Quiero dejar una configuración básica para un proyecto nuevo con este framework usando Docker.
Requerimientos
- Tener Nodejs
- Tener Docker
- Conocimientos basicos en Docker
Instalación
El primer comando que necesitamos usar es el siguiente, que se encuentra en la página oficial, para poder instalar la CLI en nuestro sistema operativo y poder generar el proyecto.
$ npm i -g @nestjs/cli
$ nest new project-name
Creando nuestro Dockerfile
Lo primero que necesitamos es crear un archivo que se encargue de levantar nuestra app en modo productivo o desarrollo. Este archivo se llamará run.sh
#!/bin/bash
echo "NODE_ENV: $NODE_ENV";
if [ "$NODE_ENV" == "prod" ]
then
echo "start prod mode...";
npm run start:prod
else
echo "start dev mode...";
npm run start:dev
fi
Este archivo lee la variable de entorno NODE_ENV y, en caso de ser el valor «prod», ejecutará el comando para correr en modo productivo; caso contrario, ejecutará nuestra app en modo desarrollo.
Luego de crear este archivo, vamos a crear nuestro archivo Dockerfile.
FROM node:20.14.0-bullseye
ENV APP_PORT 3000
ENV NODE_ENV prod
ENV WORKDIR_APP /var/prod
WORKDIR ${WORKDIR_APP}
COPY package.json .
RUN npm install
COPY . .
RUN npm run build
EXPOSE ${APP_PORT}
CMD ["bash", "run.sh"]
Configurando nuestra app
En nuestro proyecto vamos a crear una carpeta llamada config y dentro un archivo llamado envs.ts. Este archivo tendrá nuestras variables de entorno de nuestra aplicación.
export const APP_PORT = parseInt(process.env.APP_PORT, 10);
export const APP_IS_PROD = process.env.NODE_ENV === 'prod'
Luego vamos a configurar nuestro main.ts. Vamos a definir una variable llamada logLevel. Esta contendrá los niveles de log por defecto en modo productivo y agregaremos los otros en modo desarrollo. Esto es completamente configurable a sus necesidades.
Adicionalmente activaremos el versionamiento de nuestra API.
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { APP_IS_PROD, APP_PORT } from './config/envs';
import { LogLevel, Logger, VersioningType } from '@nestjs/common';
async function bootstrap() {
const logger = new Logger('bootstrap');
const logLevel: LogLevel[] = ['error', 'warn', 'fatal', 'log'];
if (!APP_IS_PROD) {
logLevel.push('debug', 'verbose'); }
const app = await NestFactory.create(AppModule, { logger: logLevel });
app.enableVersioning({type: VersioningType.URI});
await app.listen(APP_PORT);
}
bootstrap();
Construir nuestra imagen
Ahora podemos correr los siguientes comandos para construir nuestra imagen.
$ docker build -t nestjs-setup .
$ docker run -it --rm -e NODE_ENV=develop -p "3000:3000" --name nest-setup nest-setup
Ahora debemos poder acceder al puerto 3000 y ver el mensaje de Hello World!.
Agregando validaciones & Swagger
Adicionalmente voy a agregar los paquetes para las validaciones y para documentar nuestra API.
Para eso debemos entrar en nuestro contenedor que esta corriendo el siguiente comando:
$ docker exec -it nest-setup bash
Agregar las siguientes dependencias:
- Swagger
- Class validator
$ yarn add class-validator class-transformer
$ yarn add @nestjs/swagger
Para este punto, debemos actualizar nuestro package.json, ya que los cambios se realizaron dentro del contenedor y no estamos usando ningún volumen en este momento. Podemos copiar el archivo package.json desde nuestro contenedor usando docker cp. No es necesario que realice este paso, ya que al final del post tendrá todo el código con todas las dependencias.
Ahora en nuestro main vamos a agregar una configuración adicional para poder ver los errores de las validaciones cuando estemos en modo desarrollo. Este paso para ver los errores es opcional pero puede ser útil si desea aplicar otro caso de uso.
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { APP_IS_PROD, APP_PORT } from './config/envs';
import { BadRequestException, LogLevel, Logger, ValidationError, ValidationPipe, VersioningType } from '@nestjs/common';
async function bootstrap() {
const logger = new Logger('bootstrap');
const logLevel: LogLevel[] = ['error', 'warn', 'fatal', 'log'];
if (!APP_IS_PROD) {
logLevel.push('debug', 'verbose');
}
const app = await NestFactory.create(AppModule, { logger: logLevel });
app.enableVersioning({type: VersioningType.URI});
const config = new DocumentBuilder()
.setTitle('Cats example')
.setDescription('The cats API description')
.setVersion('1.0')
.addTag('cats')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('docs', app, document);
// print validation errors
app.useGlobalPipes(
new ValidationPipe({
enableDebugMessages: !APP_IS_PROD,
transform: true,
exceptionFactory: (errors: ValidationError[]) => {
const allErrorMsg = errors.flatMap((error) =>
Object.values(error.constraints),
);
if (!APP_IS_PROD) {
logger.debug('Validations errors', allErrorMsg);
logger.debug('Error schema', errors);
}
return new BadRequestException(allErrorMsg);
},
}),
);
await app.listen(APP_PORT);
}
bootstrap();
Modificando nuestro app.controller.ts.
import { Body, Controller, Get, Post } from '@nestjs/common';
import { IsString } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { AppService } from './app.service';
export class AppDto {
@ApiProperty({ example: 'demo' })
@IsString()
name: string;
}
@Controller({
version: '1',
path: 'demo',
})
export class AppController {
constructor(private readonly appService: AppService) {}
@Post()
getHello(@Body() data: AppDto): string {
return this.appService.getHello();
}
}
Ahora podemos acceder a la siguiente URL: http://localhost:3000/docs. Al enviar la solicitud, podemos ver que los datos se envían correctamente.
Si enviamos un número al momento de realizar la solicitud, podemos ver los mensajes de error también en la terminal.
Docker compose
Por ultimo podemos agregar nuestro docker-compose.yml
version: '3'
services:
nest-setup:
container_name: nest-setup
build: .
ebvironment:
- NODE_ENV=develop
ports:
- "3000:3000"
volumes:
- ./src:/var/prod/src
- ./test:/var/prod/test
- ./package.json:/var/prod/package.json
$ docker compose up
Top comments (0)