Introdução
Neste tutorial, vamos explorar como construir uma aplicação web escalável utilizando TypeScript, React e NestJS. O objetivo é criar uma arquitetura robusta que facilite tanto a manutenção quanto a escalabilidade do projeto. Abordaremos as melhores práticas para a tipagem do TypeScript e a integração de APIs utilizando o NestJS. Vamos desenvolver uma aplicação que permite realizar operações CRUD (Criar, Ler, Atualizar e Deletar) em um recurso fictício, ensinando desde a configuração inicial até a implementação de testes. Este guia é ideal para desenvolvedores que desejam aplicar conceitos modernos no desenvolvimento de aplicações web.
Etapas
Configuração do Ambiente de Desenvolvimento
Comece configurando seu ambiente de desenvolvimento com Node.js e npm (Node Package Manager). Você pode verificar as versões instaladas com `node -v` e `npm -v`. Instale o NestJS CLI globalmente com o comando `npm i -g @nestjs/cli`.
commands# Verificar versões instaladas
node -v
npm -v
# Instalar NestJS CLI
npm i -g @nestjs/cliCriação do Projeto NestJS
Utilize o NestJS CLI para criar um novo projeto. Execute o comando `nest new nome-do-projeto` e escolha as opções desejadas. Isso irá criar a estrutura básica do seu projeto NestJS.
commands# Criar um novo projeto
nest new nome-do-projetoConfiguração do TypeScript no NestJS
O NestJS já vem configurado para utilizar TypeScript por padrão. Você encontrará um arquivo `tsconfig.json` na raiz do projeto. Você pode personalizar este arquivo para atender às suas necessidades específicas, como habilitar a verificação rigorosa de tipos.
tsconfig.json{ "compilerOptions": { "target": "es2017", "module": "commonjs", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, "include": ["src/**/*.ts"], "exclude": ["node_modules"] }
Criação de um Módulo de Produtos
Crie um módulo chamado `products` com o comando `nest g module products`. Isso irá gerar a estrutura para gerenciar produtos em sua aplicação.
commands# Criar módulo products
nest g module productsCriação da Entidade Produto
Dentro do módulo `products`, crie uma entidade `Product` utilizando TypeScript. Crie um arquivo `product.entity.ts` que define a estrutura do produto, inclusive suas propriedades e tipos.
product.entity.tsimport { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity() export class Product { @PrimaryGeneratedColumn() id: number; @Column() name: string; @Column() price: number; }
Criação do Repositório de Produtos
Crie um repositório para a entidade `Product`, que permitirá interagir com a base de dados. Para isso, crie um arquivo `products.repository.ts`.
products.repository.tsimport { EntityRepository, Repository } from 'typeorm'; import { Product } from './product.entity'; @EntityRepository(Product) export class ProductsRepository extends Repository<Product> {}
Implementação do Serviço de Produtos
Implemente uma classe de serviço `ProductsService` para encapsular a lógica de negócio. Crie um arquivo `products.service.ts` que irá conter métodos para operações CRUD.
products.service.tsimport { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { ProductsRepository } from './products.repository'; import { Product } from './product.entity'; import { CreateProductDTO } from './create-product.dto'; @Injectable() export class ProductsService { constructor( @InjectRepository(ProductsRepository) private readonly productsRepository: ProductsRepository, ) {} async create(productData: CreateProductDTO): Promise<Product> { const product = this.productsRepository.create(productData); return await this.productsRepository.save(product); } async findAll(): Promise<Product[]> { return await this.productsRepository.find(); } async findOne(id: number): Promise<Product> { return await this.productsRepository.findOne(id); } async update(id: number, productData: CreateProductDTO): Promise<Product> { await this.productsRepository.update(id, productData); return this.findOne(id); } async delete(id: number): Promise<void> { await this.productsRepository.delete(id); } }
Criação do Controlador de Produtos
Crie um controlador `ProductsController` que expõe os endpoints da API. Este controlador irá mapear requisições HTTP para métodos do serviço criado anteriormente. Crie o arquivo `products.controller.ts`.
products.controller.tsimport { Controller, Get, Post, Body, Param, Put, Delete } from '@nestjs/common'; import { ProductsService } from './products.service'; import { Product } from './product.entity'; @Controller('products') export class ProductsController { constructor(private readonly productsService: ProductsService) {} @Post() async create(@Body() productData: CreateProductDTO): Promise<Product> { return this.productsService.create(productData); } @Get() async findAll(): Promise<Product[]> { return this.productsService.findAll(); } @Get(':id') async findOne(@Param('id') id: number): Promise<Product> { return this.productsService.findOne(id); } @Put(':id') async update(@Param('id') id: number, @Body() productData: CreateProductDTO): Promise<Product> { return this.productsService.update(id, productData); } @Delete(':id') async delete(@Param('id') id: number): Promise<void> { return this.productsService.delete(id); } }
Implementação de Testes Unitários
Adicione testes unitários para o serviço de produtos. Crie um arquivo `products.service.spec.ts` que utilize o framework de testes do NestJS e o Jest.
products.service.spec.tsimport { Test, TestingModule } from '@nestjs/testing'; import { ProductsService } from './products.service'; import { ProductsRepository } from './products.repository'; import { Product } from './product.entity'; import { getRepositoryToken } from '@nestjs/typeorm'; describe('ProductsService', () => { let service: ProductsService; let repository: ProductsRepository; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ ProductsService, { provide: getRepositoryToken(ProductsRepository), useValue: { create: jest.fn(), save: jest.fn(), find: jest.fn(), findOne: jest.fn(), update: jest.fn(), delete: jest.fn(), }, }, ], }).compile(); service = module.get<ProductsService>(ProductsService); repository = module.get<ProductsRepository>(getRepositoryToken(ProductsRepository)); }); it('should be defined', () => { expect(service).toBeDefined(); }); // Adicione testes para os métodos do serviço });
Executando a Aplicação e Testes
Compile e execute a aplicação utilizando o comando `npm run start`. Em seguida, utilize o Postman ou cURL para testar os endpoints da API. Para rodar os testes unitários, execute `npm run test`.
commands# Executar a aplicação
npm run start
# Executar os testes
npm run testcurl_examples# Listar todos os produtos
curl -X GET http://localhost:3000/products
# Criar um novo produto
curl -X POST -H 'Content-Type: application/json' -d '{"name":"Novo Produto", "price":99.99}' http://localhost:3000/products
Conclusão
Neste tutorial, você aprendeu a construir uma aplicação web escalável utilizando TypeScript, React e NestJS. Passamos por todo o processo desde a configuração do ambiente até a implementação das camadas de controle, serviços e repositórios. Além disso, abordamos a criação de testes unitários para garantir a qualidade do código. Com estas habilidades, você estará preparado para desenvolver aplicações robustas e manuteníveis, aproveitando as melhores práticas de tipagem e integração de APIs.