NestJS — Framework para NodeJS
Construa aplicações backend eficientes e escaláveis
Nos dias atuais o NodeJS está sendo uma escolha muito popular na criação de web services e o NestJS veio para auxiliar nesse quesito. O NestJS é um framework para construir aplicações backend em NodeJS, muito eficiente e escalável. Além de tudo ainda suporta TypeScript 😃 (mas pode ser desenvolvido em JavaScript puro também).
Como padrão, o nest utiliza o Express como servidor HTTP padrão, porém também pode ser configurado com o Fastify. Ele também possui algumas integrações com outras ferramentas como: TypeORM, Sequelize, Mongoose, Prisma, OpenAPI, etc.
Caso você já seja um desenvolvedor Java/Spring, Angular e até mesmo C#/.NET você irá se familiarizar muito com o NestJS — como a injeção de dependência, criação de módulos, etc).
Setup
Para criar um app com o nest é necessário ter o node instalado, caso você não tenha, pode obter o node clicando aqui.
Agora para começar, instale o nest globalmente e crie eu primeiro projeto.
$ npm i -g @nestjs/cli
$ nest new projeto-nestjs
Após criado o projeto terá a seguinte estrutura:
O nest irá gerar os arquivos, app.controller.ts, e uma classe de teste para controller app.controller.spec.ts, app.module.ts, app.service.ts e main.ts (arquivo de entrada do aplicativo que usa a função principal NestFactory para criar uma instância do aplicativo nest).
Controllers
Os Controllers são responsáveis por lidar com Requests e retornar Responses ao Client. Em seu projeto você poderá ter um ou mais controllers, dependendo de como você irá estruturar.
Dentro do controller você pode ter mais de uma rota, e essas rotas podem realizar diferentes ações em seu projeto.
Para criar um controller vamos precisar de uma classe e decorators (decorators associam as classes aos metadados necessários e permite que o nest crie um mapa de roteamento) .
Para definir utilizamos o decorator @Controller(), como opcional podemos passar um prefixo para a rota, e isso nos permite agrupar um conjunto de rotas relacionadas dentro de um controller.
E agora nos métodos podemos definir com os decorators: @Get(), @Post(), @Put(), @Delete() etc.
Aqui também já temos o ItemsService sendo injetado no construtor, e isso nos permite declarar e inicializar o serviço.
Providers
Os Providers são uma parte fundamental do nest. Muitas classes são tratadas como providers, são elas: services, repositories, factories, etc. Isso significa que os providers podem ser injetados como dependência.
Aqui neste service iremos ter a nossa lógica do projeto, responsável pelo armazenamento e recuperação dos dados, e será utilizado pela nossa classe do controller.
Para definir o nosso service, vamos utilizar do decorator @Injectable(), anexando os metadados, e esse decorator é gerenciado pelo IoC do nest.
Modules
Cada app possui um Module raiz, que é o ponto de partida que o nest usa para organizar a estrutura do aplicativo. Aqui utilizamos o decorator @Module().
Além do module raiz, também temos outros modules para organizar os componentes do projeto. Assim, para a maioria dos apps, a arquitetura resultante empregará vários modules, cada um encapsulando um conjunto de recursos relacionados.
Middlewares
O Middleware é uma função que é chamada antes do handler de rota. As funções tem acesso aos objetos de request e response. Então se quisermos ter acesso antes do controller, podemos criar uma classe com o decorator @Injectable() implementando a interface NestMiddleware.
Essa função pode executar as seguintes tarefas:
- Executar qualquer código.
- Fazer alterações nos objetos de request e de response.
- Encerrar o ciclo request-response.
- Chamar um outro middleware.
Para configurar o middleware, usamos o método configure() da classe do module. App que incluem middleware precisam implementar a interface NestModule no nosso arquivo de app.module.ts
Aqui no arquivo acima, habilitamos o middleware apenas para o path items e o requisições do tipo GET.
Exception Filters
O nest tem algo bem bacana aqui que seria o Exception Filters, ele vem com uma camada de exceções integrada, que é responsável por processar todas as exceções não tratadas no app. Quando isso acontece, o erro é capturado por essa camada, que envia automaticamente uma resposta amigável para o usuário que está chamando o serviço.
Caso você queira lidar com as exceções, você poderá configurar o seu próprio filtro de exceções. Para isso crie uma classe que implementa a ExceptionFilter, e adicione a sua lógica para o tratamento.
Aqui no código acima você utiliza o decorator @Catch(HttpException), com isso você informa ao nest que esse filtro específico, apenas aceita exceções do tipo HttpException e nada além disso. Esse decorator pode receber um parâmetro ou uma lista separado por vírgula, tratando vários tipos de exceções de uma só vez.
Agora você poderá definir a utilização do filter, utilizando o decorator @UseFilters(HttpExceptionFilter) no seu Controller. Ou por método (method-scoped) ou pela classe (controller-scoped).
Também podemos defini-lo como global no arquivo main.ts, ou no arquivos module (global-scoped). Assim será aplicado em todo app (todas rotas e controllers).
Pipes
Os Pipes são classes que utiliza o decorator @Injectable() e que implementam a interface PipeTransform.
Eles tem duas utilidades:
- Transformação: transforma os dados de entrada no formato desejado (ex: string para int)
- Validação: verifica se os dados de entrada são válidos, caso contrário lança uma exceção.
O nest já vem com alguns pipes e você pode utilizá-los, são eles: ValidationPipe, ParseIntPipe, ParseFloatPipe, ParseBoolPipe, ParseArrayPipe, ParseUUIDPipe, ParseEnumPipe, DefaultValuePipe, ParseFilePipe.
Você pode definir os pipes em: argumento da rota, métodos, controllers ou então globalmente.
Guards
Os Guards têm uma única responsabilidade, determinar se uma solicitação será tratada pelo handler de rotas ou não, dependendo de algumas condições (permissões, funções, ACLs, etc.) no momento da execução.
Ao contrário dos middlewares que não sabem sobre o próximo passo (next()) na cadeia, os guards sabem exatamente o que será executado em seguida.
Para criar um guard você deverá implementar a interface CanActivate e utilizar o decorator @Injectable(). Os guards são executados após todos middlewares, porém antes dos interceptores e pipes.
Um ótimo exemplo para utilização de guards seria autorização, pois você poderá definir que as rotas sejam validadas com a autenticação enviada.
Como nas pipes, os guards também podem ser definidos em: controllers, métodos, ou globalmente.
Interceptors
O Interceptor possui um conjunto de recursos bem úteis, o que faz que com ele seja possível realizar:
- Vincular uma lógica antes ou depois da execução do método.
- Transformar o resultado retornado.
- Transformar a exceção lançada.
- Estender o comportamento da função.
- Substituir uma função completamente dependendo de certas condições (ex: cache)
Para criar um interceptor, é necessário criar uma classe e utilizar o decorator @Injectable() e implementar a interface NestInterceptor.
No exemplo acima usamos para alterar o retorno, quando for null, trocamos por uma string vazia (‘’) .
Aqui também podemos definir os interceptors em: controllers, métodos, ou globalmente.
Concluindo…
Aqui aprendemos sobre como criar serviços backends eficientes com NestJS, utilizando injeção de dependência. E com a facilidade de criação de rotas utilizando seus decorators. E ainda contamos com o suporte a Typescript que nos auxilia no desenvolvimento, e reduz o número de bugs. 😃
Você pode aprender mais sobre Nest com a documentação oficial deles clicando aqui.