O que é NestJS?
NestJS é um framework NodeJS moderno que faz uso de frameworks NodeJS populares como Express e Fastify sob o capô. O NestJS foi amplamente inspirado no Angular e, como resultado, emprega um sistema de módulos no estilo Angular. NestJS é escrito em TypeScript, embora também suporte JavaScript nativo.
Pré-requisitos
Para seguir este tutorial, você deve atender aos seguintes requisitos
- Competência em PostMan ou qualquer outra ferramenta de teste de API.
- Conhecimento básico de aplicativos NodeJS e Express.
- Conhecimento básico de TypeScript.
- Competência em MongoDB (Mongoose).
O seguinte deve ser instalado em seu sistema
- NodeJS v.14 e superior.
- Código do Visual Studio (recomendado) ou qualquer outro IDE.
- PostMan ou qualquer outra ferramenta de teste de API.
Terminologias comuns usadas no NestJS;
Aqui estão alguns dos termos mais usados no NestJS que você encontrará muito neste artigo.
Interfaces
Uma interface é uma definição de tipo. Como resultado, ele é utilizado como verificador/executor de tipos em funções, classes, etc.
interface humanInterface{
name:string;
gender:string;
age:number;
}
const kevin: humanInterface={
name:'Kevin Sunders',
gender:'Male',
age: 25,
}
A
humanInterface
acima executa uma verificação de tipo estrita no kevin
objeto. O Typescript geraria um erro se você adicionasse outro campo ou alterasse o tipo de qualquer uma das propriedades do objeto. Controladores
Os controladores são responsáveis por receber as solicitações recebidas e responder ao cliente. Um controlador colabora com seu serviço associado.
Serviços
Um serviço é um provedor que armazena e recupera dados e é usado com seu controlador correspondente.
Decoradores
Um decorador é uma expressão de retorno de função que aceita um
target
, name
, e property descriptor
como argumentos opcionais. Decoradores são escritos como @decorator-name
. Eles geralmente são anexados a declarações de classe, métodos e parâmetros. @Get()
getAll(): Model[] {
return this.testService.getAll();
}
O
@Get
decorador acima marca o bloco de código abaixo dele como um GET
solicitação. Mais sobre isso mais tarde. Módulo
Um módulo é uma parte de um programa que lida com uma tarefa específica. Um módulo no NestJS é marcado anotando uma classe anotada com o
@Module()
decorador. Nest usa os metadados fornecidos pelo @Module()
decorador para organizar a estrutura do aplicativo. Instalando a CLI
Para começar, você precisará instalar o NestJS CLI ****com
npm
. Você pode pular esta etapa se já tiver o NestJS CLI instalado em seu sistema. npm i -g @nestjs/cli
Este bloco de código acima instalará a CLI Nest globalmente em seu sistema.
Criando um novo projeto
Para gerar um novo projeto, execute
nest new
seguido pelo nome do projeto desejado. Para este artigo, escreveremos uma API de blog simples com funcionalidade CRUD enquanto aderimos aos padrões RESTful. nest new Blog-Api
Este comando solicitará que você selecione um gerenciador de pacotes, escolha
npm
. Isso irá, então, estruturar toda a estrutura do projeto com um endpoint de API de teste cuja porta está definida como
3000
por padrão. Você pode testá-lo em http://localhost:3000
depois de executar o npm run start:dev
comando que iniciará o servidor no modo de observação semelhante ao que o nodemon faz em aplicativos expressos. Depois de testar o endpoint, você precisará excluir alguns dos arquivos padrão porque não precisará mais deles. Para fazer isso;
- abra a pasta src e dentro dela,
- excluir
app.controller.spec.ts
, - excluir
app.controller.ts
, - excluir
app.service.ts
, - Abra
app.module.ts
, - Remova a referência a
AppController
noscontrollers
array e as importações, - Remova a referência a
AppService
nosproviders
array e as importações.
Você também pode precisar alterar o
README.md
para atender às suas especificações. Seu
app.module.ts
arquivo deve ficar assim, //app.module.ts
import { Module } from '@nestjs/common';
@Module({
imports: [],
controllers: [],
providers: [],
})
export class AppModule {}
Variáveis Ambientais
Como boa prática, algumas informações confidenciais em seu código não devem ser tornadas públicas. Por exemplo, seu
PORT
e seu MongoDB URI
. Vamos corrigir isso no seu código.
No seu terminal, execute
npm i dotenv
Em seguida, crie um
.env
arquivo em seu diretório e adicione-o ao seu .gitignore
Arquivo. Armazene seu PORT
variável, você também terá que armazenar seu MongoDB URI
depois no mesmo lugar. Agora substitua o PORT
exposto em seu main.ts
Arquivo. Para fazer isso, importe o dotenv
pacote e chame o .config()
método nele. import * as dotenv from 'dotenv';
dotenv.config();
Este deve ser seu
main.ts
arquivo depois de seguir as etapas acima. //main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as dotenv from 'dotenv';
dotenv.config();
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(process.env.PORT);
}
bootstrap();
Gerando Módulos
Para gerar um módulo NestJS usando a CLI NestJS, execute o trecho de código abaixo.
nest generate module blogs
Este comando cria um
blogs
pasta que contém um blogs.module.ts
arquivo e registra BlogsModule
em seu app.module.ts
Arquivo. Gerando interfaces
Vamos gerar uma interface usando a CLI do NestJS para fazer a verificação de tipo do objeto que representará as postagens do seu blog. Para conseguir isso, você deve primeiro
cd
nos blogs
porque é recomendado que sejam armazenados próximos aos objetos de domínio aos quais estão associados. cd src/blogs
Em seguida, execute o trecho de código abaixo para gerar a interface.
nest generate interface blogs
isso cria um
blogs.interface.ts
Arquivo. É aqui que vamos definir nossa interface. vamos nomear a interface BlogsInterface
. export interface BlogsInterface {
title: string;
body: string;
category: string;
dateCreated: Date;
}
antes de executar mais comandos em seu terminal, lembre-se de
cd
fora do src
pasta e de volta para sua pasta raiz executando cd ../..
Gerando serviços e controladores
Você precisará gerar uma classe de serviço para armazenar e recuperar dados e lidar com toda a lógica e uma classe de controlador para lidar com todas as solicitações de entrada e respostas de saída.
Serviço
Para gerar um serviço execute o comando abaixo,
nest generate service blogs
Este comando cria dois arquivos
blogs.service.spec.ts
e o blogs.service.ts
e registra o serviço nos providers
matriz no blogs.module.ts
. Controlador
Para gerar um controlador execute o comando abaixo,
nest generate controller blogs
Este comando cria dois arquivos o
blogs.controller.spec.ts
e o blogs.controller.ts
e registra o controlador em controllers
matriz no blogs.module.ts
. Com isso sua estrutura de blogs está quase completa, você só precisa fazer o
BlogsService
acessível a outras partes do seu programa. Você pode conseguir isso criando um exports
matriz no blogs.module.ts
arquivo e registrando o BlogsService
nessa matriz. //blogs.module.ts
import { Module } from '@nestjs/common';
import { BlogsService } from './blogs.service';
import { BlogsController } from './blogs.controller';
@Module({
providers: [BlogsService],
controllers: [BlogsController],
exports: [BlogsService],
})
export class BlogsModule {}
MongoDB(Mongoose).
Instale o mangusto executando,
npm install --save @nestjs/mongoose mongoose
Após a instalação, importe
{MongooseModule}
de '@nestjs/mongoose’
em seu app.module.ts
Arquivo. Em seguida, pegue seu MongoDB URI
e armazene-o em seu .env
Arquivo. Repita as etapas para importar dotenv
no app.module.ts
Arquivo. Em seguida, nas imports
array chama o .forRoot()
método que leva seu MongoDB URI
como um argumento no MongooseModule
. Semelhante ao mongoose.connect()
em aplicativos expressos regulares. @Module({
imports: [BlogsModule, MongooseModule.forRoot(process.env.MONGODB_URI)],
Criando um esquema.
Vamos criar um esquema para definir a forma dos blogs em nossa coleção. Para fazer isso,
- Crie uma pasta dentro de seus
blogs
pasta, nomeie-aschemas
, - Dentro dos
schemas
pasta, crie um arquivo e chame-o deblogs.schema.ts
.
Então,
Em primeiro lugar, você terá que,
- Importar o
prop
decorador, oSchema
decorador e oSchemaFactory
de@nestjs/mongoose
, - Criar uma classe
Blog
e exportá-lo, - Transforme a classe em um Schema colocando o
@Schema()
decorador acima da classe, - Criar uma constante
BlogSchema
, atribua o valor de retorno de chamar o.createForClass(Blog)
com o nome de sua classe como argumento emSchemaFactory
que você importou anteriormente.
//blogs.schema.ts
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
@Schema()
export class Blog {}
export const BlogSchema = SchemaFactory.createForClass(Blog);
Em seguida, você precisará definir as propriedades do Schema.
Para definir uma propriedade no esquema, você precisará marcar cada uma delas com o
@prop()
decorador. O @prop
decorador aceita um objeto de opções ou uma declaração de tipo complexo. As declarações de tipo complexo podem ser arrays e declarações de tipo de objeto aninhado. //blogs.schema.ts
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
@Schema()
export class Blog {
@Prop({ required: true })
title: string;
@Prop({ required: true })
body: string;
@Prop({ required: true })
category: string;
@Prop({ required: true })
dateCreated: Date;
}
export const BlogSchema = SchemaFactory.createForClass(Blog);
Próxima importação
{ Document }
de 'mongoose'
. Em seguida, crie um tipo de união com a classe Schema e o
Document
importado . Igual a, //blogs.schema.ts
import { Document } from 'mongoose';
export type BlogDocument = Blog & Document;
Seu
blogs.schema.ts
final arquivo deve ficar assim, import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
export type BlogDocument = Blog & Document;
@Schema()
export class Blog {
@Prop({ required: true })
title: string;
@Prop({ required: true })
body: string;
@Prop({ required: true })
category: string;
@Prop({ required: true })
dateCreated: Date;
}
export const BlogSchema = SchemaFactory.createForClass(Blog);
Esquema de registro
Você precisará importar tudo para o seu
blogs.module.ts
Arquivo. Para conseguir isso, você precisará, - Importar
{MongooseModule}
de'@nestjs/mongoose’
, - Importar
{Blog, BlogSchema}
de'./schemas/blogs.schema’
- Crie uma
imports
array dentro do@module
decorador - Chame o
.forFeature()
método noMongooseModule
. Isso recebe um array contendo um objeto que define umname
e umschema
propriedade que deve ser definida para seuBlog.name
e seuBlogSchema
respectivamente.
@Module({
imports: [
MongooseModule.forFeature([{ name: Blog.name, schema: BlogSchema }]),
],
Injetando Esquema
Você precisará injetar o
Blog
model no blogs.service.ts
usando o @InjectModel()
decorador. Para conseguir isso você terá que - importar
{ Model }
de'mongoose'
, - importar
{ InjectModel }
de'@nestjs/mongoose’
, - Importar
{Blog, BlogDocument}
de'./schemas/blogs.schema’
, - Crie um
constructor
dentro doBlogsService
classe, - Declarar um
private
variável e chame-a deblogModel
e atribua um tipo deModel<BlogDocument>
para isso. Todos os métodos do mangusto serão chamados nesta variável.
Lembre-se disso,
BlogDocument
é o tipo de união do Blog
class e o Model
do Mongoose que você criou anteriormente. Ele é usado como o tipo genérico para sua variável. - Decore
blogModel
com@InjectModel()
e passeBlog.name
como argumento.
constructor(
@InjectModel(Blog.name)
private blogModel: Model<BlogDocument>,
) {}
Como funciona o roteamento
Até agora você deve ter notado que o
@Controller
decorador tem a string 'blogs'
passou nele. Isso significa que o controlador enviará todas as respostas e tratará de todas as solicitações feitas em http://localhost/3000/blogs
. Em seguida, você implementará o serviço e a lógica do controlador.
Lógica de serviço e controlador.
Finalmente chegou a hora de implementar sua funcionalidade CRUD.
Antes de começarmos, você precisará configurar seu controlador. Comece importando alguns
HTTP
decoradores de método em seu controlador. //blogs.controller.ts
import {
Controller,
Body,
Delete,
Get,
Post,
Put,
Param,
} from '@nestjs/common';
Em seguida, você precisará importar o serviço e registrá-lo para poder acessá-lo e importar a interface para verificação de tipo.
//blogs.controller.ts
import { BlogsInterface } from './blogs.interface';
import { BlogsService } from './blogs.service';
Para registrar seu serviço crie um
constructor
dentro do BlogsController
class e declare um private readonly
variável service
e defina seu tipo como BlogsService
. constructor(private readonly service: BlogsService) {}
Agora que você está tudo configurado, vamos começar.
Criar
Lógica de serviço
Importar
{ BlogsInterface }
de './blogs.interface'
e adicione um async
função para o BlogsService
classe chamada createBlog
, que terá um parâmetro blog
, com seu tipo como BlogInterface
, e seu tipo de retorno como uma Promise
com um <Blog>
genérico tipo. async createBlog(blog: BlogsInterface): Promise<Blog> {
return await new this.blogModel({
...blog,
dateCreated: new Date(),
}).save();
}
Lógica do controlador
Em seu
BlogsController
class adiciona um async
função para a classe. Chame de createBlog
e marque-o com o @Post
decorador que o define como um POST
request.createBlog
leva um parâmetro blog
, com seu tipo como BlogInterface
. Marque o parâmetro com @Body
decorador que extrai todo o body
objeto do req
objeto e preenche o parâmetro decorado com o valor de body
. @Post()
async createBlog(
@Body()
blog: BlogsInterface,
) {
return await this.service.createBlog(blog);
}
Leia
Adicione dois
async
métodos, um para retornar uma única postagem do blog e o segundo para retornar todas as postagens do blog. Lógica de serviço
async getAllBlogs(): Promise<Blog[]> {
return await this.blogModel.find().exec();
}
async getBlog(id: string): Promise<Blog> {
return await this.blogModel.findById(id);
}
Lógica do controlador
@Get()
async getAllBlogs() {
return await this.service.getAllBlogs();
}
@Get(':id')
async getBlog(@Param('id') id: string) {
return await this.service.getBlog(id);
}
O
async
funções são marcadas com o @Get
decorador que o define como um GET
solicitação. O segundo
async
o decorador da função tem um argumento ':id'
. Que é o que você passará para o @Param
decorador. O parâmetro é marcado com o @Param('id')
que extrai os params
propriedade da req
objeto e preenche o parâmetro decorado com o valor de params
. Atualizar
Vamos implementar a lógica para o
PUT
solicitação. Lógica de serviço
async updateBlog(id: string, body: BlogsInterface): Promise<Blog> {
return await this.blogModel.findByIdAndUpdate(id, body);
}
Lógica do controlador
@Put(':id')
async updateBlog(
@Param('id')
id: string,
@Body()
blog: BlogsInterface,
) {
return await this.service.updateBlog(id, blog);
}
O
async
o segundo parâmetro da função é marcado com o @Body()
decorador que extrai todo o body
objeto do req
objeto e preenche o parâmetro decorado com o valor de body
. Excluir
Vamos implementar a lógica para
delete
solicitações de. Lógica de serviço
async deleteBlog(id: string): Promise<void> {
return await this.blogModel.findByIdAndDelete(id);
}
A
Promise
o tipo genérico é void
porque um Delete
request retorna uma promessa vazia. Lógica do controlador
@Delete(':id')
async deleteBlog(@Param('id') id: string) {
return await this.service.deleteBlog(id);
}
Testando a API
Para testar esta API, você deve usar uma ferramenta de teste de API. Para este artigo, usarei uma ferramenta de teste de API popular chamada Postman. Usarei dados aleatórios sobre tópicos populares para testar.
Criar
Faça um
POST
solicitação para http://localhost/3000/blogs
com os seguintes objetos JSON, isso adicionará todos os dados ao seu banco de dados. {
"title": "jeen-yuhs",
"body": "The life of superstar rapper Kanye West is currently streaming on Netflix - and according to our jeen-yuhs review, it's a fascinating watch. -credit:Radio Times",
"category":"Music"
}
{
"title": "Why You Should Always Wash Your Hands",
"body": "Germs from unwashed hands can be transferred to other objects, like handrails, tabletops, or toys, and then transferred to another person's hands.-credit cdc.gov",
"category":"Health"
}
{
"title": "Why You Should Follow me on Twitter",
"body": "Well, Because I asked nicely",
"category":"Random"
}
Você deve obter um
201
resposta e o blog criado com uma data e um _id
adicionado. Leia
Faça um
GET
solicitação para http://localhost/3000/blogs
. Isso deve retornar um 200
resposta com uma matriz de todos os dados que você adicionou anteriormente. Copie o _id
propriedade de um dos objetos da matriz. Faça outro
GET
solicitação para http://localhost/3000/blogs/id
com o ID copiado anteriormente. Isso deve retornar um 200
resposta com os dados do objeto cujo id foi usado para fazer a solicitação. Atualizar
Faça um
PUT
solicitação para http://localhost/3000/blogs/id
com os dados abaixo. O id
deve ser substituído pelo que você copiou anteriormente. Isso deve retornar um 200
resposta e atualiza o objeto com o id
Por trás das cenas. se você executar outro GET
request, você deve obter o objeto atualizado. {
"title": "why you Should Cut your Nails",
"body": "It's important to trim your nails regularly. Nail trimming together with manicures makes your nails look well-groomed, neat, and tidy.- credit:WebMD",
"category":"Health"
}
Excluir
Faça um
DELETE
solicitação para http://localhost/3000/blogs/id
.Isso deve retornar um 200
resposta e exclui o objeto com o id
Por trás das cenas. se você executar outro GET
solicitação, você não verá o objeto excluído. Conclusão
Então, finalmente chegamos ao final deste artigo. Vamos recapitular o que você cobriu.
- O que é NestJS,
- Terminologias no NestJS,
- Criando um aplicativo NestJS,
- Integrando o MongoDB em um aplicativo NestJS,
- Aplicativo de manipulação e NestJS,
Isso é muito, parabéns por chegar até aqui.
Você pode encontrar o código no github.
Boa sorte em sua jornada NestJS!