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.

quinta-feira, 25 de fevereiro de 2021

Unit Test com Node, Typescript e Jest

Vamos aproveitar o post anterior (AWS Lambda, Node e Typescript), e vamos incluir no projeto o "jest", com ele conseguimos criar unit test.

Passos:

1) Inserir dependências

npm install --save-dev babel-jest @babel/core @babel/preset-env @babel/preset-typescript @types/jest babel-jest jest


2)  No elemento "scripts" do arquivo "package.json" altere para "jest" o valor do elemento "test"

3) Vamos incluir o type "jest" no arquivo "tsconfig.json"

4) Para a compilação do projeto, vamos deixar de fora o diretório que irá conter os arquivos de testes, para isso, vamos colocar o elemento "exclude" no arquivo "tsconfig.json"

5) Agora vamos criar o arquivo de configuração do babel, na raiz do projeto crie o arquivo "babel.config.js" com as seguintes configs:

6) Crie o diretório "test" e o arquivo "hello.world.test.ts"

7) Agora execute o comando

npm run test


Site do Jest - link 

O projeto deste post está no meu github link.

sexta-feira, 19 de fevereiro de 2021

AWS Lambda, Node e Typescript

Neste post vou mostrar como criar um AWS Lambda utilizando Node e Typescript. 

O projeto será organizado para o exemplo deste post e para entendimento que o mesmo projeto possa ser reaproveitado (módulos) em outros lambdas, por exemplo, um projeto que componha todas as operações/funções (lambda) de uma API.

Passos: 

1) Instalação npm:

sudo apt-get install npm

2) Instalação Node:

sudo npm cache clean -f
sudo npm install -g n
sudo n stable

3) Instalação typescript:

sudo npm install -g typescript

4) - Criação do projeto:

mkdir LambdaNodeTypescript
cd LambdaNodeTypescript
tsc --init
npm init

5) Com o projeto criado, abra o mesmo com alguma ferramenta de desenvolvimento de sua preferencia, neste exemplo será utilizado o Visual Studio Code. Execute:

code .

6) Vamos continuar as configurações agora usando o terminal do Visual Studio Code. Vamos acrescentar as seguintes dependências do projeto para desenvolvimento com o Typescript. execute os seguintes comandos:

npm install @types/node --save-dev
npm install aws-sdk --save-dev
npm install @types/aws-lambda --save-dev


7) Depois vamos configurar a parte de build, agora vamos criar as classes que irão compor nosso projeto. Vamos criar um modulo de controller, para isso crie um dir "controller" e o seguinte arquivo "hello.world.controller.ts". Nesta classe que iremos deixar nossas regra de negocio.


8) Vamos agora criar o handler, crie um dir com o nome "handler", e crie o arquivo "hello.world.handler.ts".


9) Vamos criar o index, para podermos declarar o método de execução do lambda, que no caso do exemplo é o "execute".

10) Agora vamos configurar o build e test do projeto, para o build, vamos alterar o arquivo "tsconfig.json" da seguinte maneira:


Agora vamos alterar o arquivo "package.json":


11) Execute o comando para gerar o arquivo zip.

npm run all

12) Vamos criar um AWS Lambda, acesse a console, selecione "Lambda" e depois "Create function":

 

13) Em "Function Name" preencha com "LambdaNodeTypescript", selecione a ultima runtime do node disponível e selecione a role para execução e click em "Create Function"


14) Com a função criada, vamos fazer o upload do zip do projeto, em  "Action" selecione "Upload a .zip file"


15) Click em "Upload", selecione o arquivo zip do projeto e click em "Save"


16) Vamos apontar agora para o método "execute", vá em "Runtime settings" e click em "Edit" 

17) Em "Handler" altera para "index.execute" e click em "Save"

18) Vamos criar um evento de teste para o nosso Lambda, selecione "Configure test events"

19) Em "Event name" preencha com "test1" e passe um json em branco e click em "Create"

20) Vamos testar, certifique-se que o evento de teste criado anteriormente está selecionado e click em "Test".


O projeto deste post está no meu github link.

segunda-feira, 24 de fevereiro de 2020

Criando Dominio Compacto

A criação de um domínio compacto vem desabilitado, neste post irei demonstrar como habilitar e criar um domínio SOA compacto.

Crie os schemas utilizando o RCU. (os passos são iguais ao do post Criando um Dominio no Oracle SOA Suite 12c.)

cd $ORACLE_HOME/oracle_common/bin
./rcu



Execute:

cd $ORACLE_HOME/oracle_common/common/bin
export CONFIG_JVM_ARGS=-Dcom.oracle.cie.config.showProfile=true
./config.sh

Selecione "criar um domínio compacto", e selecione a localização do domínio.


Selecione "Oracle SOA Suite - <version>"


Daqui em diante, os passos são iguais ao do post Criando um Dominio no Oracle SOA Suite 12c.







quinta-feira, 7 de fevereiro de 2019

Spring Security Oauth2 com Ldap - Resource Server

No post anterior (link), criamos o projeto para a geração dos token com o Spring Security Oauth2. Agora vamos criar o projeto que irá conter um Controller restringindo na operação "/test" somente usuários que tiver a role ROLE_DEVELOPERS.

Vamos a configuração do projeto. Crie um projeto, e no pom.xml acrescente as seguintes dependências:


Crie uma classe para as configurações de base de dados, ex.: JdbcConfiguration


Crie uma classe que vai estender a classe ResourceServerConfigurerAdapter e iremos sobrescrever os 2 métodos configure. Nestes métodos estarão as configurações necessárias para a validação do token, clientId e do usuário logado e as regras do HTTP Security. Ex.: AuthorizationClientConfiguration

Acrescente a notação @EnableResourceServer para habilitar o filtro Spring Security que autentique as request por meio do token do Oauth2.


Observe que utilizamos o bean TokenStore configurado na classe JdbcConfiguration.

Crie uma classe que vai estender a classe GlobalMethodSecurityConfiguration e iremos sobrescrever 1 método. Ex.: MethodSecurityConfiguration

Acrescente a notação @EnableGlobalMethodSecurity para habilitar a segurança do método global Spring Security.


Crie uma classe que vai implementar a classe WebMvcConfigurer. Ex.: ResourceServerWebConfiguration

Acrescente a notação @EnableWebSecurity para ter as configurações do Spring Security.


Agora vamos criar o Controller, crie uma classe ex.: TestController, acrescente a notação @Controller. E crie um método conforme o exemplo. O ponto principal é a notação @PreAuthorize, onde definimos os scopes e roles que terão permissão de execução.


Vamos ao testes.
User: developer

Gerando um token:


Executando o método /test


User: tester


Executando o método /test e acesso negado:


Log:

O projeto deste post está no meu github link.

quarta-feira, 6 de fevereiro de 2019

Spring Security Oauth2 com Ldap - Authorization Server

Vou demonstrar neste post, como configurar o Spring Security Oauth2 com o LDAP (Veja como configurar o OpenLDAP aqui), neste post vamos configurar somente o Authorization Server.

Temos a opção do inMemory para o controle do client e token, mas vou demonstrar utilizando este controle com base de dados.

Rode o script OAUTH2_SPRING_SEC.sql no Oracle XE, e terá as seguintes tabelas geradas.


Faça um select na tabela OAUTH_CLIENT_DETAILS e vai ter o clientId para test.


Vamos a configuração do projeto. Crie um projeto, e no pom.xml acrescente as seguintes dependências:


A lib do ojdbc pode ser baixada em link. e para configurar a lib execute:

mvn install:install-file -DgroupId=com.oracle -DartifactId=ojdbc7 -Dversion=12.1.0 -Dpackaging=jar -Dfile=ojdbc7.jar

Agora vamos as classe de configuração:

Crie uma classe para as configurações de base de dados, ex.: JdbcConfiguration


Crie uma classe que vai estender a classe AuthorizationServerConfigurerAdapter e iremos sobrescrever os 3 métodos configure. Nestes métodos estarão as configurações necessárias para a validação do token, clientId e do usuário logado. Ex.: AuthorizationServerConfiguration

Acrescente a notação @EnableAuthorizationServer para habilitar o Authorization Server no contexto da aplicação.


Observe que utilizamos os beans DataSource e o TokenStore configurados na classe JdbcConfiguration.

Crie uma classe que vai estender a classe WebSecurityConfigurerAdapter e iremos sobrescrever 4 métodos. Ex.: ServerSecurityConfiguration

O primeiro método a ser sobrescrito vai conter as regras para HTTP Security.


O segundo método a ser sobrescrito contem as regras para a autenticação do usuário no ldap.


O terceiro método a ser sobrescrito serve para retornar o bean UserDetailsService que é utilizado na classe AuthorizationServerConfiguration.


E o quarto método a ser sobrescrito serve para retornar o bean AuthenticationManager que é utilizado na classe AuthorizationServerConfiguration.


Agora vamos aos métodos necessários para o correto funcionamento com o Ldap. O método abaixo irá preencher o atributo "authorities" com os grupos que o usuário  é membro.


O método abaixo, utilizado para a conexão com o Ldap.


Testando:
/oauth/token
grant_type: password



/oauth/check_token

/oauth/token
grant_type: refresh_token


Log:

Validação do clientId na base de dados:


Autenticação do usuário no Ldap.


O projeto deste post está no meu github link.