Guia para Nest.js - Construindo uma API REST com Nest e Node PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

Guia para Nest.js – Construindo uma API REST com Nest e Node

Como desenvolvedor de back-end do Node.js, você concordará que, por padrão, o Node.js é muito básico e não faz suposições sobre o que você precisa ao criar um aplicativo. Como resultado, você é responsável por configurar tudo o que deseja usar em um aplicativo, incluindo lidar com roteamento, fazer chamadas de API, configurar TypeScript ou Web Sockets ou até mesmo coisas fundamentais como organização de código, estrutura de arquivos e convenções de nomenclatura. .

Gerenciar uma aplicação em larga escala pode ser uma tarefa difícil, especialmente se ela não foi projetada com uma estrutura clara e diretrizes rígidas de organização de código.

Nest.js tenta resolver alguns desses problemas criando uma abstração em torno do Node.js para que você, como desenvolvedor, possa se concentrar no problema do aplicativo, em vez de em outros pequenos detalhes de implementação.

Neste guia, você aprenderá os fundamentos básicos do Nest.js de cima a baixo, com o objetivo de deixá-lo atualizado para que possa criar aplicativos Node.js de nível empresarial com a ajuda do Nest.js rapidamente.

Tudo o que aprenderemos neste guia será incremental; cobrindo muito terreno sobre conceitos introdutórios. Para aproveitar ao máximo este guia, é útil codificar.

Vamos mergulhar de cabeça, pessoal!

Código fonte: Como de costume, você pode bifurcar e mexer no código-fonte hospedado em GitHub.

Observação: Usaremos o Postman para testar a API em nossa demonstração. Você pode baixá-lo no Página de download do carteiro. Como alternativa, você pode simplesmente usar o navegador, a linha de comando curl ou qualquer outra ferramenta com a qual você esteja familiarizado.

O que é Nest.js

Pense no Nest.js como um superconjunto do Node.js que abstrai tarefas difíceis, ferramentas e código padrão, ao mesmo tempo que adiciona um kit de ferramentas completo para o desenvolvimento de seu aplicativo usando JavaScript e TypeScript modernos.

Nest.js fornece uma arquitetura de aplicativo pronta para uso que permite que desenvolvedores e equipes criem altamente escaláveis, testáveis, fracamente acoplados e de fácil manutenção, aproveitando opções e módulos proeminentes e prontamente disponíveis na comunidade, como aqueles disponíveis em Aplicativos Express.js. Você pode até trocar o Express (que ele usa internamente por padrão) pelo Fastify, mas isso significaria que você pode precisar usar diferentes bibliotecas compatíveis com o Fastify em seu aplicativo.

Ele combina os recursos de Programação Funcional, Programação Orientada a Objetos e Programação Reativa Funcional, e com mais de 52.4 mil estrelas e 6.2 mil garfos em GitHub e uma contagem semanal de downloads de até 1,784,004, a estrutura progressiva Node.js é uma referência popular para a criação de aplicativos de servidor eficientes, escaláveis ​​e de nível empresarial.

Recursos do Nest.js

A seguir estão os motivos pelos quais o Nest.js cresceu e se tornou uma estrutura Node.js tão popular:

  1. Nest.js foi criado para ajudar os desenvolvedores a construir aplicativos monolíticos e também microsserviços.
  2. Embora seja poderoso, também é fácil de trabalhar para o desenvolvedor; fácil de usar, rápido de aprender e fácil de aplicar.
  3. Ele aproveita o TypeScript (um superconjunto de JavaScript) pronto para uso e abre espaço para os desenvolvedores escreverem código sustentável, livre de erros de tempo de execução.
  4. Possui uma interface de linha de comando que ajuda a aumentar a produtividade dos desenvolvedores e a facilidade de desenvolvimento.
  5. Ao construir com Nest.js, os processos de desenvolvimento são aprimorados e economiza tempo, esteja você inicializando um produto mínimo viável ou trabalhando em um aplicativo, porque o Nest vem com uma estrutura de pastas de projeto incrível por padrão.
  6. Ele suporta uma variedade de módulos específicos do Nest que ajudam na integração de conceitos e tecnologias comuns, incluindo TypeORM, GraphQL, registro, validação, Mongoose, WebSockets, cache, etc.
  7. Nest.js pode se orgulhar de possuir algumas das melhores documentações para qualquer estrutura existente. Sua documentação é completa, fácil de entender e útil para economizar tempo de depuração, pois é processada sem esforço quando há necessidade de solução para um problema.
  8. Nest.js se integra ao Jest, o que simplifica a gravação de testes de unidade em seus aplicativos.
  9. Ele foi desenvolvido para aplicativos empresariais de pequeno e grande porte.

Criando um projeto Nest.js

Para começar a usar Nest.js em sua máquina local, primeiro você precisa instalar a Nest Command Line Interface (CLI), que ajudaria a criar uma nova pasta de projeto Nest.js e preencher a pasta com arquivos principais e módulos necessários para um Aplicativo Nest.js.

Execute o seguinte comando para instalar a interface de linha de comando Nest.js:

$ npm i -g @nestjs/cli
// Or
$ yarn global add @nestjs/cli
// Or
$ pnpm add -g @nestjs/cli

Depois de instalar com êxito a CLI Nest.js globalmente em sua máquina local, você pode executar nest na linha de comando para ver vários comandos que podemos usar:

$ nest

Resulta em:

Usage: nest  [options]

Options:
  -v, --version                                   Output the current version.
  -h, --help                                      Output usage information.

Commands:
  new|n [options] [name]                          Generate Nest application.
  build [options] [app]                           Build Nest application.
  start [options] [app]                           Run Nest application.
  info|i                                          Display Nest project details.
  add [options]                          Adds support for an external library to your project.
  generate|g [options]  [name] [path]  Generate a Nest element.
    Schematics available on @nestjs/schematics collection:
      ┌───────────────┬─────────────┬──────────────────────────────────────────────┐
      │ name          │ alias       │ description                                  │
      │ application   │ application │ Generate a new application workspace         │
      │ class         │ cl          │ Generate a new class                         │
      │ configuration │ config      │ Generate a CLI configuration file            │
      │ controller    │ co          │ Generate a controller declaration            │
      │ decorator     │ d           │ Generate a custom decorator                  │
      │ filter        │ f           │ Generate a filter declaration                │
      │ gateway       │ ga          │ Generate a gateway declaration               │
      │ guard         │ gu          │ Generate a guard declaration                 │
      │ interceptor   │ itc         │ Generate an interceptor declaration          │
      │ interface     │ itf         │ Generate an interface                        │
      │ middleware    │ mi          │ Generate a middleware declaration            │
      │ module        │ mo          │ Generate a module declaration                │
      │ pipe          │ pi          │ Generate a pipe declaration                  │
      │ provider      │ pr          │ Generate a provider declaration              │
      │ resolver      │ r           │ Generate a GraphQL resolver declaration      │
      │ service       │ s           │ Generate a service declaration               │
      │ library       │ lib         │ Generate a new library within a monorepo     │
      │ sub-app       │ app         │ Generate a new application within a monorepo │
      │ resource      │ res         │ Generate a new CRUD resource                 │
      └───────────────┴─────────────┴──────────────────────────────────────────────┘

Aqui, você verá como usar os comandos e agora pode tocar no new|n [options] [name] comando para criar seu primeiro projeto Nest.js:

$ nest new getting-started-with-nestjs
// Or
$ nest n getting-started-with-nestjs

A seguir, será perguntado qual gerenciador de pacotes você gostaria de usar:

? Which package manager would you ❤️ to use? (Use arrow keys)
  npm
  yarn
> pnpm

Fique à vontade para escolher o gerenciador de pacotes de sua preferência, irei com pnpm. Isso ocorre porque ele é cerca de três vezes mais eficiente e rápido que o NPM e, com um sistema de cache rápido, o PNPM também é mais rápido que o Yarn.

Após escolher um gerenciador de pacotes, o processo de instalação continua, então o aplicativo Nest.js seria criado.

Agora você pode cd no projeto recém-criado e abra-o com um editor de sua escolha:

$ cd getting-started-with-nestjs

Com o projeto agora criado, podemos executá-lo com qualquer um dos seguintes comandos:

$ npm run start
// Or
$ yarn start
// Or
$ pnpm run start

Se você der uma olhada no package.json arquivo, você notará no segmento de script, o valor para pnpm run start is nest start:


    
"start": "nest start",

Isso significa que você também pode executar o aplicativo Nest.js executando:

$ nest start

Uma olhada na estrutura do projeto Nest.js

Vamos dar uma olhada em como um aplicativo Nest é estruturado:

/package.json

A package.json file é o coração do Node.js e, por extensão, do projeto Nest.js. Ele contém todos os metadados sobre o projeto e define várias propriedades funcionais do projeto que são necessárias para instalar dependências de aplicativos ou executar scripts de projeto.

Já vimos a capacidade do start script.

A start:dev profile permite observar alterações na aplicação e recarregá-la automaticamente, sem a necessidade de parar a aplicação e reiniciá-la – e é destinado ao desenvolvimento. O start:prod O script é útil quando você deseja testar se seu aplicativo está pronto para produção, bem como quando você o implanta na produção, junto com outros scripts para testar o aplicativo Nest.js.

@nestjs/platform-express define express como o servidor HTTP padrão em um aplicativo Nest.

/tsconfig.json

A tsconfig.json file é um arquivo escrito em JSON (JavaScript Object Notation) que define as opções relacionadas ao TypeScript necessárias para compilar o aplicativo Nest.

/nest-cli.json

Ele contém metadados necessários para criar, organizar ou implantar aplicativos Nest.

/test

Este diretório contém todos os arquivos necessários para executar os testes do Nest. Nest usa a estrutura Jest para testar com configuração Jest no jest-e2e.json arquivo.

/src

A src directory é a pasta pai do núcleo do projeto Nest. Ele contém o main.ts file que é o arquivo onde o aplicativo Nest é iniciado. O trabalho do main.ts arquivo é para carregar AppModule que é importado de /src/app.module.ts.

Posteriormente neste guia, aprenderemos sobre Módulos; um dos principais componentes de um aplicativo Nest.js.

A AppModule é uma classe criada como um módulo, usando o @Module decorador. No app.module.ts arquivo, AppService da ./app.service e AppController da ./app.controller também são importados.

A AppController também é uma classe criada usando o @Controller decorador, enquanto o AppService é uma classe criada usando o @Injectable anotação.

O legal do Nest é que ele tem poucos decoradores que adicionam metadados a qualquer classe e esses metadados definem o propósito dessa classe, de forma que:

  • @Controller()transforma uma classe em um controlador.
  • @Module() transforma uma classe em um módulo.
  • @Injectable() transforma uma classe em um provedor.

Também no src diretório é o app.controller.spec.ts arquivo, que é um arquivo de teste para controladores.

Podemos executar o aplicativo usando nest start.

O aplicativo começa às http://localhost:3000 no seu navegador:

Guia para Nest.js - Construindo uma API REST com Nest e Node PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

Podemos alterar o conteúdo que aparece em http://localhost:3000, indo até o app.service.ts arquivo, onde o provedor da rota de índice foi definido.

Os blocos de construção de um aplicativo Nest.js

Existem três componentes principais de um aplicativo Nest.js:

  1. Módulos
  2. controladores
  3. prestadores

Ao aprender sobre os blocos de construção de um aplicativo Nest, vamos primeiro limpar o projeto Nest, excluindo o app.controller.spec.ts, ./app.service, app.module.ts e ./app.controller arquivos; saindo apenas main.ts, para emular um ciclo de vida de desenvolvimento do zero.

Neste ponto, quando removemos o importado AppModule arquivo de main.ts, somos informados de que um argumento para ‘módulo’ não foi fornecido.

Para demonstrar os blocos de construção de um aplicativo Nest, daremos uma olhada em uma implementação simples de perfil de usuário, construindo uma API REST para lidar com operações CRUD em um objeto.

Módulos

No src pasta crie um novo app.module.ts arquivo e, em seguida, crie um AppModule classe, que exportamos.

A seguir, importe o AppModule classe em main.ts, e corra nest start.

Navegar para http://localhost:3000 no seu navegador e você receberá um erro 404:

Guia para Nest.js - Construindo uma API REST com Nest e Node PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

Isso ocorre porque ainda não definimos uma rota para o URL base do app Nest.

Back in app.module.tsnós temos o AppModule class que temos ainda não é um módulo Nest. Para torná-lo um módulo Nest, adicionamos o @Module() decorador importado de @nestjs/commonentão passamos um objeto vazio.



import { Module } from '@nestjs/common';
@Module({})

export class AppModule {}

Agora temos um módulo Nest.js!

Observação: Um módulo é uma classe anotada com um @Module() decorador.

Cada aplicativo Nest possui um módulo raiz, que serve como ponto de entrada para resolver a estrutura e os relacionamentos de um aplicativo Nest.

É altamente recomendável usar vários módulos para organizar os componentes da sua aplicação.

A @Module() decorador permite que os desenvolvedores definam metadados sobre uma classe no aplicativo Nest.

No caso de existirem vários módulos, como módulo de usuários, módulo de pedidos, módulo de chat, etc., o app.module.ts deve ser usado para registrar todos os outros módulos do app Nest.

Criando Rotas; Controladores

Os controladores são necessários para criar rotas em aplicativos Nest. A finalidade de um controlador é receber solicitações específicas para um aplicativo Nest; controlar o ciclo de solicitação e resposta para várias rotas dentro do aplicativo.

Quando uma solicitação HTTP é feita do cliente para o aplicativo Nest, a rota que corresponde à rota em que a solicitação está sendo feita trata a solicitação e retorna a resposta apropriada.

Para criar um controlador em um app Nest, temos que usar o @Controller() decorador.

No src diretório, crie um novo arquivo app.contoller.ts, e aí podemos definir um controlador Nest:

import { Controller } from '@nestjs/common';

@Controller({})

export class AppController {}

É isso! Temos um controlador muito bom, mas para criar uma nova rota, precisamos primeiro informar nosso aplicativo Nest sobre o controlador criado.

Para conseguir isso, certificamo-nos de importar AppController em app.module.ts e defina informações sobre os controladores em @Module() decorador – como uma matriz de controladores:



import { Module } from '@nestjs/common';
import { AppController } from './app.controller';

@Module({
  controllers: [AppController],
})

export class AppModule {}

Manipulando Solicitações GET

Então definimos um simples getUser() percurso (com o @Get() decorador usado para lidar com solicitações HTTP GET para um caminho especificado) para servir como rota base, podemos acessar o mesmo no navegador em https://localhost:3000:



import { Controller, Get } from '@nestjs/common';

@Controller({})

export class AppController {
  @Get()
  getUser() {
    return 'I am a great person';
  }
}

Isto resulta em:

Guia para Nest.js - Construindo uma API REST com Nest e Node PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

Hmm, aqui estamos retornando apenas uma string, mas e se quiséssemos retornar um objeto? Em vez de uma string, podemos definir um objeto:



import { Controller, Get } from '@nestjs/common';

@Controller({})

export class AppController {
  @Get()
  getUser() {
    return { name: 'Uchechukwu Azubuko', country: 'Nigeria' };
  }
}

Navegar para http://localhost:3000 no seu navegador e você verá o objeto:

Guia para Nest.js - Construindo uma API REST com Nest e Node PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

Longe da rota base, que tal criar uma rota semelhante a http://localhost:3000/user para buscar todos os usuários?

Podemos criar um controlador para lidar com essa rota de duas maneiras.

Uma maneira seria definir um novo método, usando o @Get() decorador/manipulador.

import { Controller, Get } from '@nestjs/common';

@Controller({})

export class AppController {
  @Get()
  getUser() {
    return { name: 'Uchechukwu Azubuko', country: 'Nigeria' };
  }
}

Nest.js fornece decoradores ou manipuladores para todos os vários métodos HTTP, incluindo @Get(), @Post(), @Put(), @Delete(), @Patch(), @Options() e @Head().

A @All() decorator define um endpoint que lida com todos os vários métodos.

Tratamento de solicitações POST

Também podemos definir solicitações POST para armazenamento de dados no banco de dados, usando o @Post() decorador:

import { Controller, Post } from '@nestjs/common';

@Controller({})
export class AppController {
  @Post()
  store() {
    return 'Post request successful';
  }
}

Em seguida, testamos a solicitação POST usando Postman e notamos que a string é retornada com sucesso conforme definido.

Guia para Nest.js - Construindo uma API REST com Nest e Node PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

Você pode perguntar: e se eu também quiser fazer mais do que retornar dados? Talvez, para enviar dados.

Para isso, você precisa injetar os dados dentro do método route, conforme mostrado:

import { Controller, Post, Req } from '@nestjs/common';
import { Request } from 'express';

@Controller({})
export class AppController {
  @Post()
  store(@Req() req: Request) {
    return req.body;
  }
}

Agora, ao testarmos a solicitação POST com o Postman, podemos visualizar os dados que estão sendo enviados. Neste caso, é apenas um objeto vazio:

Confira nosso guia prático e prático para aprender Git, com práticas recomendadas, padrões aceitos pelo setor e folha de dicas incluída. Pare de pesquisar comandos Git no Google e realmente aprender -lo!

Guia para Nest.js - Construindo uma API REST com Nest e Node PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

Roteamento dinâmico com parâmetros de rota

Suponha que você queira aceitar dados dinâmicos como parte de uma solicitação. Primeiro, precisamos definir o token no caminho da rota, para anotar a posição dinâmica na rota/URL, depois usando o @Param() decorator, o parâmetro route pode ser acessado assim:

import { Controller, Get, Param } from '@nestjs/common';

@Controller({})
export class AppController {
  @Get('/:userId')
  getUser(@Param() userId: number) {
    return userId;
  }
}

A userId é retornado com sucesso:

Guia para Nest.js - Construindo uma API REST com Nest e Node PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

Tratamento de solicitações assíncronas

Nest.js é capaz de lidar com solicitações assíncronas que retornam uma promessa usando várias abordagens:

import { Controller, Get} from '@nestjs/common';

@Controller({})
export class AppController {
  @Get()
  async findAll(): Promise {
    return [];
  }
}

Na abordagem acima, a assincronicidade é tratada usando o async palavra-chave. Outra abordagem é retornar fluxos observáveis ​​RxJS:

import { Controller, Get} from '@nestjs/common';

@Controller({})
export class AppController {
  @Get()
  findAll(): Observable {
    return of([]);
  }
}

Aqui, Nest.js assinará a fonte nos bastidores e, quando o stream for concluído, pegará o último valor emitido automaticamente.

Tratamento de redirecionamentos no Nest

A @Redirect() decorador é usado para redirecionar uma resposta para um URL diferente. O @Redirect() decorator aceita dois argumentos – o URL para o qual redirecionar e o código de status no redirecionamento, ambos opcionais:

import { Controller, Get} from '@nestjs/common';

@Controller({})
export class AppController {
  @Get()
  @Redirect('https://www.ucheazubuko.com', 302)
  getSite() {
    return { url: 'https://stackabuse.com' };
  }
}

Código de status de retorno

Para retornar o código de status de qualquer solicitação tratada no servidor Nest.js, o @HttpCode(…) passa facilmente.

No Nest, o código de status padrão para solicitações GET é 200, uma solicitação POST é 201, uma solicitação de erro é 304

O código de status para uma solicitação do servidor pode ser definido conforme mostrado abaixo:

import { Controller, Post, HttpCode } from '@nestjs/common';

@Controller({})
export class AppController {
  @Post()
  @HttpCode(204)
  create() {
    return 'This action adds a new user to the app.';
  }
}

Tratamento de solicitações DELETE

Semelhante a fazer uma solicitação POST, uma solicitação de exclusão pode ser tratada da seguinte forma:

import { Controller, Delete, Param } from '@nestjs/common';

@Controller({})
export class AppController {
  @Delete('/:userId')
  delete(@Param() params: { userId: number }) {
    return params;
  }
}

Tratamento de solicitações UPDATE

Uma solicitação para atualizar dados específicos no servidor pode ser tratada usando o @Patch() decorador:

import { Controller, Patch, Req} from '@nestjs/common';
import { Request } from 'express';

@Controller({})
export class AppController {
  @Patch('/:userId')
  update(@Req() req: Request) {
    return req.body;
  }
}

Agora que vimos várias maneiras de definir controladores típicos que frequentemente teríamos em um servidor robusto, é importante observar que o controlador deve ser enxuto, limpo e definido por caso de uso, de modo que se houver outro controlador para definir user rotas, então um diretório separado deve ser criado e dedicado para lidar com as mesmas - longe do AppController.

Então em user.controller.ts, podemos configurar todos os manipuladores de rota para serem prefixados com /user/ escrevendo o código como mostrado abaixo:



import { Controller, Get } from '@nestjs/common';

@Controller('/user')
export class UserController {
  @Get()
  getUser() {
    return 'I am from the user controller';
  }
}

A seguir, cadastre-se UserController nas matrizes dos controladores em app.modules.ts:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { UserController } from './controllers/user/user.controller';

@Module({
  controllers: [AppController, UserController],
})

export class AppModule {}

Quando navegamos para https:localhost:3000/user, ele retorna com sucesso:

Guia para Nest.js - Construindo uma API REST com Nest e Node PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

Para manter a pasta do projeto ainda mais organizada do que está agora, podemos definir um user.module.ts arquivo onde definiremos o UserController:

import { Module } from '@nestjs/common';
import { UserController } from './user.controller';

@Module({
  controllers: [UserController],
})

export class UserModule {}

Em seguida, importe UserModule para dentro app.module.ts:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { UserModule } from './user/user.module';

@Module({
  controllers: [AppController],
  imports: [UserModule],
})

export class AppModule {}

Com isso poderemos ter o mesmo efeito de antes.

Observação: O Nest facilita a (g)geração de (módulos) e (controladores) usando o nest g mo e nest g co comandos. Módulos específicos, como o user módulo e controladores também podem ser criados rapidamente usando o Nest CLI, executando os comandos: nest g mo user – para criar um módulo de usuário, e nest g co user – para criar um controlador de usuário.

prestadores

Toda a busca de dados de um banco de dados deve ser feita por provedores, e não por controladores, para criar uma camada de abstração entre o código voltado para o usuário e o código que interage com dados potencialmente confidenciais. Entre essas camadas – a validação pode ser configurada para garantir o manuseio adequado do banco de dados. Com o Nest CLI, podemos criar provedores gerando serviços:

$ nest g s user

Isso cria um UserService onde definiríamos toda a lógica de negócios para o UserController, De modo que UserController lida apenas com solicitações e respostas. Em user.service.ts, vemos que o @Injectable() decorador é usado para definir a classe. No Nest, o uso do @Injectable() decorador é transformar serviços, repositórios ou classes auxiliares em um provedor.

Os provedores são injetados em uma classe por meio de seu construtor. Vamos dar uma olhada em um exemplo.

Anteriormente, em user.controller.ts, definimos a lógica de negócios para obter o objeto de usuário, mas agora devemos definir o mesmo no UserService:



import { Controller, Injectable } from '@nestjs/common';

@Controller({})

export class AppController {
  @Injectable()
  get() {
    return { name: 'Uchechukwu Azubuko', country: 'Nigeria'; };
  }
}

Em seguida, no user.controller.ts arquivo, vamos definir um construtor no UserController aula. Neste construtor, fornecemos um privado userService, que é um tipo de UserService aula. É com esse private que podemos aproveitar a lógica de negócios que definimos anteriormente para buscar os usuários:



import { Controller, Get } from '@nestjs/common';
import { UserService } from './user.service';

@Controller('/user')
export class UserController {
  constructor(private userService: UserService) {}
  @Get()
  getUser() {
    return this.userService.get();
  }
}

Assim, o UserController classe, agora depende do UserService classe em um conceito conhecido como
Injeção de dependência.

Da mesma forma, a lógica em ambos user.controller.ts e user.service.ts os arquivos são atualizados de acordo:



import {
  Controller,
  Delete,
  Get,
  Param,
  Patch,
  Post,
  Req,
} from '@nestjs/common';
import { Request } from 'express';
import { UserService } from './user.service';

@Controller('user')
export class UserController {
  constructor(private userService: UserService) {}
  @Get()
  getUsers() {
    return this.userService.get();
  }
  @Get('/:userId')
  getUser(@Param() param: { userId: number }) {
    return this.userService.getUser(param);
  }
  @Post()
  store(@Req() req: Request) {
    return this.userService.create(req);
  }
  @Patch('/:userId')
  update(@Req() req: Request, @Param() param: { userId: number }) {
    return this.userService.update(req, param);
  }
  @Delete()
  delete(@Param() param: { userId: number }) {
    return this.userService.delete(param);
  }
}


import { Injectable } from '@nestjs/common';
import { Request } from 'express';

@Injectable()
export class UserService {
  get() {
    return { name: 'Uchechukwu Azubuko', country: 'Nigeria' };
  }
  getUser(param: { userId: number }) {
    return param;
  }
  create(req: Request) {
    return req.body;
  }
  update(req: Request, param: { userId: number }) {
    return { body: req.body, param };
  }
  delete(param: { userId: number }) {
    return param;
  }
}

Agora, vamos verificar se os endpoints funcionam como deveriam, usando o Postman.

Desmistificando a injeção de dependência em Nest.js

Ao construir componentes menores de um aplicativo, como uma classe ou módulo, sua classe pode depender da funcionalidade de outra classe ou módulo, por exemplo, a necessidade de acessar um serviço HTTP fornecido por uma classe diferente para fazer chamadas de API, ou camadas de serviço que interagem com a camada de persistência.

As dependências podem ser fornecidas nos controladores por meio de Injeção de dependência.

A injeção de dependência é um conceito e padrão de programação que expressa como partes de um aplicativo são entregues a outras partes do aplicativo que as necessitam, de forma a fornecer alta coesão, mas acoplamento fraco.

O Nest oferece suporte à injeção de dependência e você pode usá-lo em seus aplicativos Nest para aprimorar a modularidade do seu projeto.

Uma ilustração prática é representada assim:

Suponha que a classe A use alguma funcionalidade da classe B. Então diz-se que a classe A depende da classe B. Assim, para usar a classe B na classe A, precisamos primeiro criar uma instância da classe B (ou seja, criar um Objeto de classe B): const b = new B ().
Transferir a tarefa de criar uma instância de uma classe para outra classe e usar diretamente a dependência da classe fornecida (o componente injetor) é conhecido como injeção de dependência.

Conselho: A injeção de dependência, ou DI, é um dos conceitos fundamentais em frameworks como Spring Boot, Nest.js e Angular.js, se quiser ler mais sobre isso, você pode conferir o documentação oficial do Angular.

Normalmente, uma classe deve se concentrar apenas em cumprir suas funções, em vez de ser usada para criar vários objetos que ela possa exigir ou não.

Benefícios da injeção de dependência.

  1. Ajuda nos testes de unidade.
  2. Com a injeção de dependência, o código padrão é reduzido, pois a inicialização das dependências é feita pelo componente injetor.
  3. O processo de extensão de um aplicativo fica mais fácil.
  4. A injeção de dependência ajuda a permitir o acoplamento fraco.

Explorando cargas úteis de solicitação

Lembre-se de que em vários manipuladores de solicitação, como POST e PATCH, conseguimos acessar a solicitação enviada pelo servidor usando o método @Req() decorador. No entanto, há mais nisso.

Em vez de recuperar todo o objeto de solicitação, podemos apenas acessar partes específicas do objeto de solicitação que precisamos.
Assim, o Nest fornece vários decoradores que podem ser usados ​​com os manipuladores de rotas HTTP para acessar objetos Express do Fastify:

Decoradores de ninho Objeto Fastify ou Express que é acessado
`@Request(), @Req()` `req`
`@Resposta(), @Res()` `re“s`
`@Próximo()` `próximo`
`@Sessão()` `req.sessão`
`@Param(param?:string)` `req.params` / `req.params[param]`
`@Body(param?:string)` `req.body` / `req.body[param]`
`@Query(param?:string)` `req.query` / `req.query[param]`
`@Headers(param?:string)` `req.headers` / `req.headers[param]`
`@Ip()` `req.ip`
`@HostParam()` `req.hosts`

Um exemplo típico seria a substituição do @Req() decorator que usamos anteriormente para ter acesso ao corpo do resultado, com o @Body() que já pode nos dar acesso direto ao corpo de uma solicitação sem perfurar:



@Post()
store(@Body() body: any) {
  return this.userService.create(body);
}

@Patch('/:userId')
update(@Body() body: any, @Param() param: { userId: number }) {
  return this.userService.update(body, param);
}


create(body: any) {
  return body;
}

update(body: any, param: { userId: number }) {
  return { body: body, param };
}

Em alguns casos, talvez você queira recuperar apenas propriedades específicas de uma carga de solicitação. Nesse caso, você teria que definir um esquema de Data Transfer Object (DTO). O esquema de transferência de dados é um objeto que define uma cópia do objeto que está sendo recuperado, mas é usado principalmente para transferir os dados entre o objeto que deve ser salvo ou recuperado e a camada de persistência. Normalmente, como esse processo é mais vulnerável a ataques, o DTO não contém tantos pontos de dados confidenciais. Esta característica também permite recuperar apenas determinados campos de um objeto.

No Nest, é recomendado o uso de classes para definir um Objeto de Transferência de Dados, pois o valor das classes é preservado durante a compilação.

Supondo que o corpo da solicitação possua um token e você não queira recuperar ou atualizar tais dados, então um DTO pode ser definido conforme mostrado abaixo:



@Patch('/:userId')
update(
  @Body() updateUserDto: { name: string; email: string },
  @Param() param: { userId: number },
) {
  return this.userService.update(updateUserDto, param);
}


update(
  updateUserDto: { name: string; email: string },
  param: { userId: number },
) {
  return { body: updateUserDto, param };
}

No entanto, você notará que definimos o tipo para updateUserDto duas vezes; em user.service.ts e em user.controller.ts, mas precisamos manter nossos códigos DRY (Don't Repeat Yourself) para não nos repetirmos na base de código.

Para isso, em uma nova pasta /user/dto no /user diretório, precisamos criar um arquivo /update-user.dto.ts com o .dto.ts extensão onde definimos e exportamos o UpdateUserDto classe para uso no user.service.ts e user.controller.ts arquivos:



export class UpdateUserDto {
  name: string;
  email: string;
}

...
import { UpdateUserDto } from './dto/update-user.dto';

@Patch('/:userId')
update(
  @Body() updateUserDto: UpdateUserDto,
  @Param() param: { userId: number },
) {
  return this.userService.update(updateUserDto, param);
}

...
import { UpdateUserDto } from './dto/update-user.dto';

update(updateUserDto: UpdateUserDto, param: { userId: number }) {
  return { body: updateUserDto, param };
}

Pipe e validação

Suponha que seja necessário validar os dados obtidos quando uma solicitação é feita no servidor.

No Nest, podemos testar a exatidão de quaisquer dados que entram ou saem do aplicativo usando pipes instalando duas dependências – class-validator e class-transformer.

Um pipe é uma classe definida com o @Injectable() decorador (portanto, pipes são provedores), que implementa o PipeTransform interface. Eles transformam os dados no formato desejado e avaliam os dados de forma que, se os dados forem considerados válidos, eles passam inalterados; caso contrário, uma exceção é lançada. Para usar um pipe, você precisa vincular uma instância da classe de pipe específica ao contexto apropriado.

A class-validator pacote permite validar decoradores e não decoradores, utilizando validador.js internamente. Enquanto o class-transformer O pacote torna possível transformar objetos em instâncias de uma classe, transformar classe em objeto e serializar ou desserializar objetos com base em determinados critérios.

Os oito tubos fornecidos pela Nest são:

  • ValidationPipe
  • ParseArrayPipe
  • ParseIntPipe
  • ParseUUIDPipe
  • ParseBoolPipe
  • DefaultValuePipe
  • ParseEnumPipe
  • ParseFloatPipe

Para demonstrar a validação no Nest neste guia, usaremos o recurso integrado ValidationPipe que torna possível impor a validação em cargas úteis de solicitação e combina bem com o class-validator pacote; regras específicas são declaradas com anotações simples nas declarações de objeto de transferência de dados/classe local em cada módulo.

Para começar a usar o integrado ValidationPipe que é exportado de @nestjs/common, vamos instalar o class-validator e class-transformer pacotes:

$ npm i --save class-validator class-transformer
# Or
$ yarn add class-validator class-transformer
# Or
$ pnpm install class-validator class-transformer

Em seguida, navegue para main.ts onde vamos ligar ValidationPipe no nível raiz do aplicativo para garantir que todos os endpoints do nosso aplicativo estejam protegidos contra a recuperação de dados inválidos:



import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe());
  await app.listen(3000);
}

bootstrap();

A seguir, nas declarações do Data Transfer Object de cada módulo, adicionamos algumas regras de validação declarando as verificações de dados apropriadas para cada dado individual. No nosso caso, declararíamos regras de validação apropriadas para name e email in UpdateUserDto:



import { IsEmail, IsString } from 'class-validator';

export class UpdateUserDto {
  @IsString()
  name: string;

  @IsEmail()
  email: string;
}

A @IsString() decorador verifica se um dado dado é uma string real, e o @IsEmail() o validador verifica se um determinado dado é um email, caso contrário, ele retorna falso e lança uma exceção.

Agora, se tentarmos fazer uma PATCH solicitação para um perfil de usuário e insira um número em vez de um e-mail válido, por exemplo, uma exceção será lançada:

Guia para Nest.js - Construindo uma API REST com Nest e Node PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

Com eles, temos uma validação muito bacana em nosso app Nest.

Ao validar com ValidationPipe, também é possível filtrar nossas propriedades que não queremos que nosso manipulador de método receba. Por exemplo, se nosso manipulador espera apenas name e email propriedades, mas uma solicitação também inclui um country propriedade, podemos remover o country propriedade do objeto resultante definindo whitelist para true quando nós instanciarmos ValidationPipe:



import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true,
    }),
  );
  await app.listen(3000);
}

bootstrap();

Vinculação de tubos no nível de parâmetro do método

Os tubos também podem ser definidos em params, também. Para isso, vincularemos o pipe no nível de parâmetro do método.

Até agora, embora tenhamos definido o userId para ser um número, você notará que se fizermos uma solicitação com o userId como uma string, será bem-sucedido independentemente:

Guia para Nest.js - Construindo uma API REST com Nest e Node PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

Para garantir que o valor de userId deve ser sempre um número, declararemos que ele vincula o getUser() manipulador de método com uma verificação de validação que garante o mesmo:


...
import { ParseIntPipe } from '@nestjs/common';

@Get('/:userId')
getUser(@Param('userId', ParseIntPipe) userId: number) {
  return this.userService.getUser(userId);
}


getUser(userId: number) {
  return { userId };
}

A ParseIntPipe define o ParseInt Pipe integrado e garante que os dados contra os quais ele é executado devem ser um número inteiro.

Agora, quando fazemos um GET solicitação para um inválido userId da string “ab”, a validação falha e uma exceção é lançada com um 400 código de estado:

Guia para Nest.js - Construindo uma API REST com Nest e Node PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

Mas com um valor numérico, a validação passa com sucesso:

Guia para Nest.js - Construindo uma API REST com Nest e Node PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

Também podemos atualizar outros manipuladores de métodos de acordo para garantir a validação adequada:



import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  ParseIntPipe,
  Patch,
  Post,
  Req,
} from '@nestjs/common';
import { Request } from 'express';
import { UserService } from './user.service';

@Controller('user')
export class UserController {
  constructor(private userService: UserService) {}
  @Get()
  getUsers() {
    return this.userService.get();
  }
  @Get('/:userId')
  getUser(@Param('userId', ParseIntPipe) userId: number) {
    return this.userService.getUser(userId);
  }
  @Post()
  store(@Req() req: Request) {
    return this.userService.create(req);
  }
  @Patch('/:userId')
  update(
    @Body() updateUserDto: { name: string; email: string },
    @Param('userId', ParseIntPipe) userId: number,
  ) {
    return this.userService.update(updateUserDto, userId);
  }
  @Delete()
  delete(@Param('userId', ParseIntPipe) userId: number) {
    return this.userService.delete(userId);
  }
}


import { Injectable } from '@nestjs/common';
import { Request } from 'express';
import { UpdateUserDto } from './dto/user-update.dto';

@Injectable()
export class UserService {
  get() {
    return { name: 'Uchechukwu Azubuko', country: 'Nigeria' };
  }
  getUser(userId: number) {
    return { userId };
  }
  create(req: Request) {
    return req.body;
  }
  update(updateUserDto: UpdateUserDto, userId: number) {
    return { body: updateUserDto, userId };
  }
  delete(userId: number) {
    return { userId };
  }
}

Agora, garantimos a técnica de melhores práticas para validar os dados que chegam ao nosso aplicativo, talvez de uma fonte externa, a qualquer momento.

Conclusão

Neste guia, você aprendeu sobre o que há de mais recente no bloco Node.js; Nest.js e tudo o que é necessário para ajudá-lo a começar se desejar criar um aplicativo com ele. Você aprendeu o que é o Nest, seus recursos, como criar um projeto Nest, como lidar com os dados recebidos em um aplicativo Nest e como validar os dados recebidos. Ao todo, você aprendeu sobre os blocos de construção de qualquer aplicativo Nest e o valor que cada componente traz para um aplicativo Nest.js.

A partir deste ponto, ainda há muito a aprender em relação à construção de um aplicativo de nível empresarial com o Nest, mas você conseguiu cobrir com sucesso os conceitos fundamentais que podem colocá-lo em funcionamento para tudo o que está por vir.

Fique atento a um novo guia no futuro, onde aprenderemos como construir uma API tranquila com Nest e MySQL.

Obrigado pela leitura!

Recursos adicionais

Documentos Nest.js
Documentos angulares

Carimbo de hora:

Mais de Abuso de pilha