segunda-feira, 8 de janeiro de 2024

Desenvolvendo uma API TO-DO com Nest.js, Swagger, MySQL e Docker

 Este guia apresenta o passo a passo para construir uma API de lista de tarefas (TO-DO) usando Nest.js, Swagger para documentação, integração com MySQL e a implementação de Docker para facilitar a configuração e execução.

Passo 1: Configurando o Ambiente

Antes de iniciarmos, certifique-se de ter o Node.js e o npm instalados. Em seguida, instale o Nest.js globalmente utilizando o seguinte comando:

npm install -g @nestjs/cli

Agora, crie um novo projeto Nest.js:

nest new nest-todo-api
cd nest-todo-api

Vamos remover os arquivos abaixo que não serão necessários.

rm ./src/app.controller.spec.ts
rm ./src/app.controller.ts
rm ./src/app.service.ts

Passo 2: Instalando Dependências

Instale as dependências necessárias para a nossa API:

npm install @nestjs/swagger swagger-ui-express mysql2 typeorm @nestjs/typeorm

Passo 3: Criando o Módulo, Controller, Service e a Entidade para Tarefas

Agora, vamos criar um módulo, controller, service para as tarefas e uma entidade para representar a tabela no banco de dados:

nest generate module task
nest generate controller task
nest generate service task
nest generate class task/task.entity

Passo 4: Configurando o Banco de Dados e Entidade

Vamos configurar a conexão com o MySQL utilizando o TypeORM. No arquivo src/app.module.ts, adicione o seguinte trecho para configurar o banco de dados:

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TaskModule } from './task/task.module';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'mysql',
      port: 3306,
      username: 'seu_usuario',
      password: 'sua_senha',
      database: 'todo_db',
      entities: [__dirname + '/**/*.entity{.ts,.js}'],
      synchronize: true,
    }),
    TaskModule,
  ],
})
export class AppModule {}

No arquivo task.entity.ts, adicione a seguinte definição de entidade:

import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity()
export class Task {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  @Column({ nullable: true })
  description: string;
}

No arquivo task.module.ts, adicione a seguinte definição:

import { Module } from '@nestjs/common';
import { TaskController } from './task.controller';
import { TaskService } from './task.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Task } from './task.entity/task.entity';

@Module({
  imports: [TypeOrmModule.forFeature([Task])],
  controllers: [TaskController],
  providers: [TaskService]
})
export class TaskModule {}

Passo 5: Implementando Operações CRUD

O service é responsável por interagir com o banco de dados para executar operações relacionadas às tarefas. Adicione a seguinte implementação em task.service.ts:

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Task } from './task.entity/task.entity';

@Injectable()
export class TaskService {
  constructor(
    @InjectRepository(Task)
    private taskRepository: Repository&ltTask&gt,
  ) {}

  async findAll(): Promise&ltTask[]&gt {
    return await this.taskRepository.find();
  }

  async create(task: Task): Promise&ltTask&gt {
    return await this.taskRepository.save(task);
  }

  async update(id: number, updatedTask: Task): Promise&ltTask&gt {
    const existingTask = await this.taskRepository.findOne(id);

    if (!existingTask) {
      throw new Error('Tarefa não encontrada');
    }

    existingTask.title = updatedTask.title;
    existingTask.description = updatedTask.description;

    return await this.taskRepository.save(existingTask);
  }

  async remove(id: number): Promise&ltvoid&gt {
    const existingTask = await this.taskRepository.findOne(id);

    if (!existingTask) {
      throw new Error('Tarefa não encontrada');
    }

    await this.taskRepository.remove(existingTask);
  }
}

No arquivo task.controller.ts, implemente as operações CRUD:

import { Controller, Get, Post, Body, Put, Param, Delete } from '@nestjs/common';
import { TaskService } from './task.service';
import { Task } from './task.entity/task.entity';

@Controller('tasks')
export class TaskController {
  constructor(private readonly taskService: TaskService) {}

  @Get()
  findAll(): Promise&ltTask[]&gt {
    return this.taskService.findAll();
  }

  @Post()
  create(@Body() task: Task): Promise&ltTask&gt {
    return this.taskService.create(task);
  }

  @Put(':id')
  update(@Param('id') id: string, @Body() task: Task): Promise&ltTask&gt {
    return this.taskService.update(+id, task);
  }

  @Delete(':id')
  remove(@Param('id') id: string): Promise&ltvoid&gt {
    return this.taskService.remove(+id);
  }
}

Passo 6: Integrando Swagger

No arquivo main.ts, adicione as configurações do Swagger:

import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const config = new DocumentBuilder()
    .setTitle('API TO-DO Nest.js')
    .setDescription('Documentação da API de TO-DO')
    .setVersion('1.0.0')
    .build();

  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, document);

  await app.listen(3000);
}
bootstrap();

Passo 7: Configurando Dockerfile e Compose

Crie o arquivo Dockerfile na raiz do projeto e adicione as configurações:

FROM node:20

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 3000

CMD ["npm", "run", "start"]

Crie o arquivo docker-compose.yml na raiz do projeto e adicione as configurações:

version: '3'
services:
  # MySQL Service
  mysql:
    image: mysql:latest
    environment:
      MYSQL_ROOT_PASSWORD: root_senha
      MYSQL_DATABASE: todo_db
      MYSQL_USER: seu_usuario
      MYSQL_PASSWORD: sua_senha
    ports:
      - "3306:3306"
    networks:
      - app-network

  # Nest.js App Service
  nest-app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    depends_on:
      - mysql
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

Passo 8: Executando o Projeto

Execute o comando abaixo:

docker compose up

Acesse o Swagger em http://localhost:3000/api para visualizar e testar as operações CRUD da sua API TO-DO.

O projeto deste post está no meu github link.