CRUD com NestJS usando Postgres
O NestJS é um framework back-end baseado no popular framework front-end Angular como sua inspiração, o seu uso é na linguagem Typescript. E como no Angular traz um CLI para construção de modules, controllers, services, guards, e muito mais, nos poupando de escrever aquelas linhas de códigos repetitivas. No entanto, suas inspirações não acabam aí, seus controllers e modo de mapear as rotas se parecem muito com o framework back-end com linguagem Java, o Spring.
O framework back-end NestJS nos ajuda no desenvolvimento de aplicações bem mais eficientes utilizando o Node.js. Por padrão o NestJS usa o TypeScript e é bem semelhante a sintaxe do Angular, e o mais curioso que o NestJS usa o “express” internamente como framework web.
A progressive Node.js framework for building efficient, reliable and scalable server-side applications.
Os requisitos de nossa API
A ideia é que ao final dessa série tenhamos um projeto com as seguintes funcionalidades:
- Criação de conta para usuários externos
- Criação de conta para usuários Administradores
- Autenticação e Autorização
- Envio de emails para confirmação de cadastro
- Envio de emails para recuperação de senha
- Busca de usuários com filtros e paginação
- Busca de dados de um usuário específico
- Bloquear o acesso de um usuário
- Manter um log do que acontece no servidor
Nossa API utilizará o PostgreSQL como banco de dados e usaremos o Docker para facilitar nossa vida de desenvolvedores. Nessa primeira parte do tutorial vamos tratar da configuração do ambiente e criação do nosso primeiro endpoint, “Criação de conta para usuários Administradores”.
Instalação do NestJS
A primeira coisa que você precisa ter instalada na sua máquina é, claro, o Node.js (e junto com ele, o npm). Para isso basta seguir as instruções no site.
É necessário instalar o NestJS de forma global.
Com o Node e o npm instalados, vamos começar a instalar a CLI (Command Line Interface ) do NestJS. Para isso, basta executar o seguinte comando no terminal:
$ npm i -g @nestjs/cli
Caso tenha algum problema durante a instalação, certifique-se de que tem as permissões necessárias, ou utilize o sudo se estiver no Linux e for necessário. Com o pacote da linha de comando instalado, podemos utilizá-lo para criar nosso projeto:
$ nest new nestjs-pgsql-api
Utilizando a CLI do Nest para criar um projeto, escolha o NPM, YARN ou PNPM com gerenciador de pacote.
nest new project-name
Após a execução deste comando, basta selecionar o npm como gerenciador de dependências. Caso prefira poderá utiliar o yarn, entretanto durante o restante deste tutorial utilizaremos o npm. Quando o comando finalizar de gerar a base de seu projeto, podemos entrar dentro do diretório:
$ cd nestjs-pgsql-api
Abrindo o projeto, você se depara com essa estrutura de pastas:
Estrutura de pastas do NestJS
Podemos observar entre os arquivos gerados a pasta src, onde ficará nosso código e a pasta test para arquivos de teste. Você pode já rodar o seguinte comando para ter certeza de que tudo está ok:
$ npm run start:dev
No browser ou qualquer programa de consumo de API(Postman, Insomnia), faça uma requisição GET para a URL http://localhost:3000. Teremos o nosso ‘Hello World’, para modificar isso, vamos no arquivo src/app.service.ts, modificamos o retorno para trazer a versão de nossa API.
import { Injectable } from '@nestjs/common';@Injectable()
export class AppService {
getHello(): any {
return {
version: '1.0.0',
description: 'CRUD com NestJS e NestJSX'
};
}
}
No browser ou qualquer programa de consumo de API(Postman, Insomnia), faça uma requisição GET para a URL http://localhost:3000. Teremos o nosso ‘Hello World’, para modificar isso, vamos no arquivo src/app.service.ts, modificamos o retorno para trazer a versão de nossa API.
import { Injectable } from '@nestjs/common';@Injectable()
export class AppService {
getHello(): any {
return {
version: '1.0.0',
description: 'CRUD com NestJS e NestJSX'
};
}
}
Fazemos de novo a requisição GET para http://localhost:3000. Observamos que assim como o Angular a injeção de dependência é feita com decorators.
Para continuar vamos instalar as dependências do TypeORM e NestJSX.
npm i --save pg typeorm @nestjs/typeorm @nestjsx/crud @nestjsx/crud-typeorm class-transformer @nestjs/swagger
Importamos no nosso app.module.ts o item que vai observar as modificações e criação da nossa classe.
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm'
import { ConsumerModule } from './consumer/consumer.module';@Module({
imports: [TypeOrmModule.forRoot()],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Criamos na raiz do projeto o aquivo ormconfig.json para utilizar nosso banco, um JSON com a seguinte configuração.
{
"type": "postgres",
"host": "localhost",
"port": 5432,
"username": "‹seu usuario›",
"password": "‹sua senha›",
"database": "‹seu banco›",
"entities": ["dist/**/*.entity.js"],
"synchronize": true,
"logging": true
}
Agora vamos criar nossa classe, utilizando a CLI do Nest — apesar desse comando não nos poupar tanto de escrever — passamos uma flag para não escrever o teste, pois não utilizaremos teste nesse tutorial.
nest g class model/consumer.entity --no-spec
Com nossa classe passamos os decorators do TypeORM para a criação da tabela na nossa base.
import { Entity, Column, PrimaryGeneratedColumn} from 'typeorm'@Entity()
export class Consumer{
@PrimaryGeneratedColumn('uuid')
id: string @Column({length: 65})
name: string @Column({length: 65})
email: string
}
Já com nossa classe, vamos criar o module, com o comando.
nest g module consumer
Vamos abrir o arquivo do nosso module, no caso consumer.module.ts dentro da pasta consumer, para implementar mais um item do Nest para o TypeORM. Assim, injeta a nossa classe para que nós possamos conseguir utilizar o seu repositório que virá pronto.
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Consumer } from 'src/model/consumer.entity';
import { ConsumerController} from './consumer.controller';
import { ConsumerService} from './consumer.service';@Module({
imports: [ TypeOrmModule.forFeature([Consumer])]
})
export class ConsumerModule {}
controllers e providers, não precisa se preocupar, pois se utilizar o CLI ele será escrito automaticamente no module.
Mais um comando do CLI, para criar nosso service, onde ficarão as funções que intermediarão com nosso repositório.
nest g service consumer --no-spec
Dentro da pasta consumer, vamos encontrar nosso consumer.service.ts.
Utilizando o @InjectRepository já temos o nosso repositório pronto com as principais funções e um pouco mais. Estendendo da classe TypeOrmCrudService, temos as funções para intermediar com nosso repositório, colocando todas as funções no service para o essencial de um CRUD.
import { Injectable } from '@nestjs/common';
import { Consumer } from 'src/model/consumer.entity';
import { TypeOrmCrudService } from '@nestjsx/crud-typeorm'
import { InjectRepository } from '@nestjs/typeorm';@Injectable()
export class ConsumerService extends TypeOrmCrudService<Consumer>{
constructor(@InjectRepository(Consumer) repo) {
super(repo)
}
}
Agora a última coisa para funcionar nossa API, o nosso controller que é criado com a CLI com o seguinte comando.
nest g controller --no-spec
De novo na pasta consumer, encontramos o consumer.controller.ts.
Eu gosto de deixar os recursos no plural, por isso acrescentei um ‘s’ em ‘consumer’ no decorator @controller. Agora siga com a atenção no construtor, a variável service, deve se chamar service, se não ocorrera um erro de undefined nas funções do service. Para finalizar, use o decorator @Crud com a configuração passada. Esse decorator que trará todas as rotas assim como mostrado na documentação, para utilizar todas as funções essenciais de um CRUD, e mais uma rota que permite o cadastro de vários registros, no nosso caso de vários consumers numa requisição só.
import { Controller } from "@nestjs/common";
import { Crud } from '@nestjsx/crud'
import { Consumer } from "src/model/consumer.entity";
import { ConsumerService } from "./consumer.service";@Crud({
model: {
type: Consumer
},
params: {
id: {
field: 'id',
type: 'uuid',
primary: true
}
}
})
@Controller('consumers')
export class ConsumerController {
constructor(public service: ConsumerService) {}
}
Configuração do PostgreSQL com o Docker
Utilizaremos o Docker em conjunto com o Docker Compose para configuração do nosso ambiente. Para manter o foco deste tutorial, não abordarei como instalar e configurar estas ferramentas. Instruções podem ser encontradas nas documentações oficiais ou em diversos tutoriais na internet. Caso não deseje utilizar o Docker para configuração do ambiente pode instalar o PostgreSQL em sua máquina e configurar um usuário e senha para acesso. O importante é você ter acesso a um banco de dados Postgre.
Vamos adicionar um arquivo docker-compose.yml na raiz de nosso projeto com as configurações de nossos containers.
docker-compose.yml
Você irá notar que também adicionei um container rodando o adminer. Adminer é um sistema de gerenciamento de banco de dados (SGBD) escrito em PHP, bem leve e com todas as funcionalidades que precisaremos neste tutorial.
Para testar se está tudo ok, vamos subir nossos containers:
$ docker-compose up -d
O comando pode demorar um pouco para executar caso ainda não tenha as imagens na sua máquina. Quando ele finalizar, você poderá acessar o adminer em http://localhost:8080/. Selecione o sistema PostgreSQL, informe o servidor, que no caso será o nome que demos ao nosso container: pgsql. Informe o nome de usuário e senha, pguser e pgpassword respectivamente. Caso tenha substituído estes parâmetros em seu arquivo docker-compose.yml certifique-se de substituí-los aqui também. Clique em Entrar.
Se tudo estiver correto você será redirecionado para a página de gerenciamento de banco de dados. Nela, clique em “Criar Base de dados” e crie o banco de dados nestjs, que usaremos no decorrer deste tutorial.
Conectando com o Banco de Dados
Precisamos agora conectar nossa aplicação NestJS com o banco de dados que acabamos de criar. Para tal utilizaremos o TypeORM. Vamos então instalar os pacotes necessários:
$ npm i --save typeorm @nestjs/typeorm pg
Agora, antes de começarmos a escrever nosso código, vamos fazer uma pequena limpeza no nosso projeto, removendo alguns arquivos que não serão utilizados.
- Podemos apagar a pasta test por completo
- Remover app.controller.spec.ts em src
- Remover app.controller.ts em src
- Remover app.service.ts em src
Lembre-se também de remover os respectivos imports no arquivo app.module.ts:
Como deverá ficar sem app.module.ts
Sua estrutura de pastas deve ter ficado dessa forma:
Estrutura após remoção de arquivos
Agora dentro da pasta src vamos criar uma pasta configs e dentro dela vamos criar o arquivo typeorm.config.ts:
typeorm.config.ts
Vamos adicionar agora o módulo do TypeORM aos imports globais, no arquivo app.module.ts.
app.module.ts
Após isso, rode novamente o comando para executar o projeto:
$ npm run start:dev
Se tudo estiver ok o projeto deve inicializar normalmente. Caso ocorra algum erro verifique se seguiu corretamente os passos até o momento.
Criando nosso primeiro endpoint
Nosso primeiro endpoint será referente à criação de um usuário com privilégios de administrador. Para tal, primeiro vamos criar nosso módulo de usuários, o módulo do nosso sistema responsável por tratar todas as questões que envolvam os usuários. Para adicionar um novo módulo ao projeto, basta executar o seguinte comando:
$ nest g module users
O que este comando faz? Ele cria uma pasta no diretório src com o nome do módulo escolhido e dentro dela cria um arquivo de declaração do módulo, nesse caso com o nome user.module.ts, e já adiciona a dependência no arquivo app.module.ts.
Dentro da nova pasta users vamos criar o arquivo user.entity.ts. Este arquivo terá a declaração da estrutura do nosso Usuário e será com base nele que o TypeORM irá gerar a tabela no banco de dados.
user.entity.ts
Observe os campos createdAt e updatedAt. Esses campos serão preenchidos automaticamente pelo TypeORM com timestamp de quando o objeto foi criado/alterado.
Podemos agora executar o comando:
$ npm run start:dev
E quando o projeto terminar de compilar, podemos acessar o painel do Adminer e dentro do nosso banco de dados nestjs veremos que foi criada a tabela user com a estrutura que descrevemos no users.entity.ts.
Vale a pena investir muito tempo com Nest.js?
Não é Next.js. É Nest.js, um framework de desenvolvimento web que é uma das tecnologias que mais gostei de conhecer em 2020. Eu gosto muito de trabalhar criando webservices com expressjs, porém o problema de trabalhar com várias linguagens e frameworks é que você sempre vai sentir falta de algo que existia naquela outra tecnologia. No meu caso, eu sentia falta de criar APIS REST de um modo parecido com o que eu criava com Java EE e de trabalhar com banco de dados de forma parecida com o que eu trabalhava com Active Record.
O Nest.js foi criado por
e já tem mais de 30 mil estrelas no github. Para ter uma ideia do tamanho atual do framework, o expressjs, que já é um framework mais antigo e, inclusive, é usado pelo Nest.js, tem 50 mil estrelas.
A documentação nos traz diferentes formas de estruturar a aplicação com Nest. Apis Rest, microserviços, Graphql, Websockets, Standalone entre outras. É um framework muito versátil.
- Definição do controller na classe pai, em localhost a chamada a qualquer endpoint dessa classe terá o prefixo ‘/cats’.
- O decorator Get() nos diz que esse método aceitará o método HTTP do tipo Get.
- Por default, será retornado um JSON com status da requisição 200.
- Existe uma injeção de dependência no construtor da classe para injetar a classe AppService.
Modularização
Esse conceito vai soar bem familiar para aqueles utilizam Angular para o desenvolvimento do client-side de suas aplicações. No NestJS toda a aplicação é desenvolvida em forma de módulos. Desta forma, um controller só terá acesso a um service caso ambos estejam explicitamente registrados no mesmo módulo.
A modularização é uma boa prática no desenvolvimento de aplicações empresariais uma vez que permite uma visualização mais clara da ligação existente entre as partes que compõem a aplicação. Além disso, uma vez modularizada, é mais fácil separar a aplicação em partes menores, o que pode viabilizar a criação de microservices, caso seja necessário.
Documentação
Por último mas não menos importante, a documentação do NestJS é excelente e explica de maneira detalhada a utilização de boa parte dos recursos disponíveis no framework, o que torna sua adoção ainda mais simples.
Além disso, caso tenha alguma dúvida que não é esclarecida pelos docs, os mantedores do NestJS são bem receptivos, então é só detalhar o problema no GitHub que com certeza algum desenvolvedor mais experiente estará disposto a te ajudar.
Antes de entrar bem afundo no nestjs, vale a pena saber mais sobre o node
Na JSConf 2009 Européia, um programador jovem chamado Ryan Dahl, apresentou um projeto em que estava trabalhando. Este projeto era uma plataforma que combinava a máquina virtual JavaScript V8 da Google e um laço de eventos. O projeto apontava para uma direção diferente das outras plataformas em JavaScript que rodam no servidor: todos I/O primitivos são orientado a evento. Aproveitando o poder e a simplicidade do Javascript, isso tornou tarefas difíceis de escrever aplicações assíncronas em tarefas fáceis. Desde quando foi aplaudido de pé no final do seu discurso, o projeto de Dahl tem recebido uma popularidade e uma aprovação sem precedentes.”
Instalação
A instalação é bem simples. Se você estiver no Windows, basta baixar o instalador no site oficial e executá-lo. Caso você esteja no Linux, como eu, pode utilizar um gerenciador de pacotes de acordo com a sua distribuição. Como eu uso o Ubuntu, precisei rodar apenas o seguinte comando:
$ curl -fsSL https://deb.nodesource.com/setup_14.x | sudo -E bash -
$ sudo apt-get install -y nodejs$ npm -v
6.14.13
$ node -v
v14.17.1
Alô, mundo
Vamos começar com o exemplo mais básico de desenvolvimento. Para isso, utilize o seu editor de texto favorito (o meu é o Visual Studio Code) e crie um diretório chamado alomundo contendo um arquivo chamado index.js:
O arquivo index.js é um arquivo que contém um código Javascript (extensão .js). O Node.js será capaz de executar esse código através do seguinte comando (digite no terminal de sua preferência):
$ node index.jsAlô, mundo!
O que aconteceu? O Node executou o código Javascript no lado do servidor (na sua máquina, nesse caso). Esse detalhe é importante pois ao codificar no Node, você não poderá utilizar variáveis e funções que existem no lado do cliente (nos navegadores
// É uma ótima ferramenta para fazer o lado back-end do seu código que remete a validar e retornar informações de um banco de dados por exemplo.
Breve Resumo sobre o node e oque é uma api
Acima tem um vídeo que indicamos para que entenda oque é o node.
AQUI UM BREVE RESUMO DO SITE DO NODEBR :
“é uma plataforma construída sobre o motor JavaScript do Google Chrome para facilmente construir aplicações de rede rápidas e escaláveis. Node.js usa um modelo de I/O direcionada a evento não bloqueante que o torna leve e eficiente, ideal para aplicações em tempo real com troca intensa de dados através de dispositivos distribuídos.
Oque é uma api?
Recursos Necessários
- Node.js — É indicado baixar a versão 10.13.0
- npm — Normalmente já vem instalado com o node.js
- postman ou insomnia para fazer requisições na api
- Um editor de texto (Indicado vsCode)
Como verificar a versão ou se está instalado ?
node -v && npm -v
Se retornar algo como 10.3.0 está tudo certo.
Planejando uma api
Como em qualquer projeto profissional é necessário pensar na arquitetura do seu projeto, a melhor forma de fazer isso é se fazer algumas perguntas :
- Qual o objetivo dessa api?
- Quais dados serão salvos e requisitados dessa api?
- Oque vai acessa-la?
Próximos conteúdos
O intuito deste post é esquentar as turbinas. O meu planejamento contempla os seguintes tutoriais.
1 . CRUD Básico Nest.js com Mongoose
2. CRUD Básico Nest.js com Postgresql
3. Separando a aplicação Nest em módulos
4. CRUD básico usando typescript.
5. Tratamento de erros e built-in exceptions
6. Autenticação e autorização com Nest.js
Quanto tempo você já passou para fazer um CRUD básico como esse? Para se aprofundar no NestJS, NestJSX, e TypeORM leiam suas documentações, assim poderão fazer mais que CRUDs básicos.