Antônio: Boas-vindas a mais um curso de NestJS. Meu nome é Antônio Evaldo e vou ser uma das pessoas instrutoras que vai te acompanhar nesse curso.
Antônio Evaldo é um homem de pele clara, olhos escuros e cabelos escuros encaracolados. Usa óculos de grau com armação arrendondada, bigode e cavanhaque. Está com os cabelos amarrados atrás da cabeça e um moletom azul-escuro.
Camila: Olá! Meu nome é Camila Pessôa.
Camila Pessôa é uma mulher de pele clara, olhos escuros e cabelos escuros e ondulados na altura do ombro. Tem traços arredondados. Usa óculos de grau com armação arrendondada. Está com fone de ouvido sem frio e camisa preta.
Se você já desenvolve API com NestJS e TypeORM, esse curso foi pensado para você que deseja se aprofundar nessas tecnologias!
O que vamos encontrar nesse curso, Evaldo?
Antônio: Vamos começar a partir do projeto do curso anterior e aplicar migrações, assim, você vai entender porque e como utilizá-las.
Além disso, vamos avançar nos conceitos de relacionamentos do TypeORM. Utilizaremos relacionamentos mais avançados na prática, como, por exemplo, ManyToMany
.
Mais para frente, vamos realizar um tratamento de erros mais robusto da API, utilizando boas práticas do Nest. Por fim, vamos refinar essa API com boas práticas do mercado para finalizar o projeto com êxito.
Mas, como vamos implementar todos esses conhecimentos no projeto?
Camila: Vamos trabalhar com um projeto que você já desenvolveu nos cursos anteriores da formação NestJS que era um e-commerce. No entanto, agora o nosso e-commerce já está mais robusto e seu nome é Compree.
Além de já ter uma marca, essa aplicação também já possui várias pessoas usuárias e produtos. Por isso, nosso desafio é permitir que essas pessoas consigam realizar compras no e-commerce.
O Antônio Evaldo e eu entramos nesse time agora. Por isso, com todo esse processo, precisamos de um Trello para organizar as tarefas, como acontece em um ambiente real de desenvolvimento.
Antônio: Para aproveitar melhor os conhecimentos desse curso, é importante que você já tenha feito os dois cursos anteriores da formação: Nest.js: criando uma API Restful e Nest.js: persistindo dados com TypeORM e PostgreSQL.
Esses cursos abordam os conhecimentos de Rest API com NestJS e TypeORM. Além disso, você precisa ter familiaridade com o TypeScript que é a linguagem que essas duas ferramentas utilizam.
Por fim, você também precisa estar familiarizado com o padrão de arquitetura MVC que utilizamos nos cursos anteriores.
Aproveite o fórum para tirar dúvidas e o Discord da Alura para interagir com outros alunos e alunas. Vamos estudar?
Antônio: Antes de começar a desenvolver as novas funcionalidades, vamos retomar o projeto da API da loja Compree.
Vamos simular a entrada de duas novas pessoas no time: Camila e eu. Dessa forma, aprendemos o que fazer quando alguém embarca em um projeto que já tem até banco de dados.
Ainda que você não tenha o projeto na sua máquina, você pode seguir os passos que vamos te mostrar agora.
O primeiro passo é baixar o projeto do GitHub e abri-lo no VSCode.
Em seguida, abrimos o terminal integrado com "Ctrl + `" e digitar o comando para instalar as dependências do projeto:
npm install
Inclusive, se você quiser saber mais detalhes, as dependências estão especificadas no arquivo package.json
.
O próximo passo é configurar as variáveis de ambiente. Como é uma etapa importante em todo projeto, a empresa pode disponibilizar essas informações para você.
Na raiz do projeto, vamos criar um arquivo .env
e colar as seguintes informações:
.env
:
DB_HOST=127.0.0.1
DB_PORT=5432
DB_USERNAME=root
DB_PASSWORD=root
DB_NAME=db_lojá
DB_ADMIN_EMAIL=admin@root.com
Essas informações vão ser utilizadas, principalmente, pelo arquivo docker-compose.yaml
. Nele, temos as variáveis utilizadas como DB_USERNAME
e DB_PASSWORD
que são dados sensíveis e, por isso, ficam nesse arquivo oculto .env
.
docker-compose.yaml
:
version: '3.5'
services:
postgres:
image: postgres:latest
environment:
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_PASSWORD: ${DB_PASSWORD}
PGDATA: /data/postgres
volumes:
- postgres:/data/postgres
ports:
- "5432:5432"
networks:
- postgres
restart: unless-stopped
pgadmin:
image: dpage/pgadmin4
environment:
PGADMIN_DEFAULT_EMAIL: ${DB_ADMIN_EMAIL}
PGADMIN_DEFAULT_PASSWORD: ${DB_PASSWORD}
ports:
- "8081:80"
depends_on:
- postgres
networks:
- postgres
# código omitido…
Em seguida, vamos executar o docker-compose.yaml
, utilizando o Docker na sua máquina.
Toda a infraestrutura necessária está na atividade "Preparando o Ambiente", onde especificamos a versão do NodeJS e Docker.
Como estamos utilizando Windows, vamos abrir o Docker Desktop. Com isso, já conseguimos nos comunicar com o Docker através do terminal integrado do VSCode. Caso queira, podemos limpar a tela com "Ctrl + L".
docker-compose up -d
Após dar "Enter", vamos criar todas as dependências listadas pelo Docker que são: o banco de dados do postgres
e o Postgres pgadmin
.
Em ports
na linha 24 do docker-compose.yaml
, conseguimos descobrir que o Postgres Admin pode ser aberto na porta 8081 do localhost.
No navegador, vamos digitar na barra de endereço:
localhost:8081
Na primeira vez que abrimos o pgAdmin pode demorar um pouco para carregar a página. Nela, vamos colocar o e-mail e a senha para login, os quais foram configurados no arquivo .env
.
- E-mail: admin@root.com
- Senha: root
Com isso, logamos no pgAdmin e podemos manipular o banco de dados. Contudo, ainda não tem nenhum servidor listado no painel de "Object Explorer" à esquerda.
O servidor que vai ser utilizado pela nossa aplicação Nest tem que ser configurado corretamente. Não apenas o servidor, como o banco de dados e as tabelas.
Será que vamos precisar criar tudo do zero?
Começamos a entender um dos problemas enfrentados por pessoas que estão entrando em um novo time e precisam estar em congruência com as pessoas que já estão no projeto e tem um banco de dados, tabelas e registros configurados.
Já vamos te mostrar uma solução para essa problemática. Mas, por enquanto, temos que fazer alguns passos obrigatórios quando configuramos o banco de dados pela primeira vez na nossa máquina.
Você já fez esses passos no curso anterior, mas quem embarca em um novo projeto também precisa seguir esses passos.
Primeiro, na parte de "Quick Links" do dashboard, clicamos em "Add New Server". Na aba "General" da janela aberta, vamos nomear o servidor como db_server
. Pode ser o nome que você desejar.
Na aba "Connection", é obrigatório colocar o campo "Host name/address" como postgres
. Enquanto no campo "username", vamos substituir o nome de usuário admin
por root
. No campo "password", definimos a senha como root
.
Vamos manter o campo "port" com a posta 5432
, pois também está configurada no nosso projeto.
Por fim, vamos clicar no botão "Save" no canto inferior direito.
Com isso, o servidor db_server
foi criado. No painel de "Object Explorer", vamos expandir "db_server > Databases" ao clicar na seta à esquerda de seus nomes.
Por padrão, temos dois banco de dados criados: postgres
e root
. Agora, precisamos criar um banco de dados exatamente com o nome especificado no arquivo .env
: o db_loja
.
Para isso, clicamos com o botão direito em Databases
, selecionamos a opção "Create > Database". Na janela que se abre, vamos definir o nome do banco de dados como db_loja
no campo "Database". Novamente clicamos no botão "Save".
Agora, temos listado o banco de dados db_loja
no "Object Explorer". Expandimos "db_loja > Schemas > public > Tables" para perceber que não tem nenhuma tabela criada.
Novamente nos deparamos com um contratempo por não ter nenhuma tabela ou registro criado. Como resolver esse problema para embarcar no projeto e ficar alinhado com o time?
Existe uma forma automática e que já está configurada no projeto para resolver essa questão. A seguir, vamos mostrar as vantagens e desvantagens dessa forma, bem como qual será a solução que se aplica melhor para o nosso caso. Te esperamos no próximo vídeo!
Antônio: Vamos continuar a retomada do nosso projeto. Já configuramos as variáveis de ambiente, Docker e Postgres Admin. Mas, nos deparamos com uma situação problemática onde as tabelas que ainda não foram criadas.
Como fazemos para nos alinhar com o restante do time que já está essas configurações de banco de dados implementadas em suas máquinas?
Para mostrar uma solução mais robusta para esse problema, vamos ir até o cartão "Criar Migrations" na coluna "Backlog" onde temos os cartões prontos para iniciar no Trello organizado pelo time.
Nesse cartão, temos a seguinte informação sobre o projeto:
O Banco de Dados da Compree ainda não possui migrations e está apenas utilizando a opção
synchronize: true
no Data Source do TypeORM. Essa opção é viável para uso apenas em ambiente de desenvolvimento, pois pode gerar perda de dados em produção.Vamos implementar as migrations na nossa API para que haja um versionamento de BD mais robusto.
sincronize: true
Vamos mostrar a opção synchronize: true
no código para que você possa entender melhor. No VSCode, vamos até "src > config > postgres.config.service.ts
". Esse arquivo que criamos no curso anterior faz a integração do NestJS com TypeORM.
postgres.config.service.ts
:
@Injectable()
export class PostgresConfigService implements TypeOrmOptionsFactory {
constructor(private configService: ConfigService) {}
createTypeOrmOptions(): TypeOrmModuleOptions {
return {
type: 'postgres',
host: this.configService.get<string>('DB_HOST'),
port: this.configService.get<number>('DB_PORT'),
username: this.configService.get<string>('DB_USERNAME'),
password: this.configService.get<string>('DB_PASSWORD'),
database: this.configService.get<string>('DB_NAME'),
entities: [__dirname + '/../**/*.entity.{js,ts}'],
syncronize: true,
};
}
}
Nele, temos uma classe PostgresConfigService
com o decorator @Injectable
que implementa a TypeOrmOptionsFactory
. Também tem um constructor
que pega um configService
, esse código já se relaciona com as variáveis de ambiente e como podem ser utilizadas no NestJS.
Por fim, temos o método createTypeOrmOptions()
que retorna um objeto que se relaciona ao TypeORM. Esse objeto é o que chamamos de Data Source (fonte de dados) que contém as informações mais importantes para que o TypeORM consiga trabalhar.
Por isso, é nessa fonte de dados que informamos o banco de dados que estamos utilizando, o postgres
. Também informamos o host, a porta, o nome de usuário, senha, o banco de dados e até as entidades que serão utilizadas pelo TypeORM.
Note que a última opção se chama syncronize
e tem valor true
. Essa opção faz o trabalho de criar automaticamente as tabelas e, por vezes, alguns registros a depender do volume guardado no Docker quando rodamos a API.
Se rodamos a API, as tabelas vão ser criadas automaticamente de acordo o esquema de entidades do TypeORM.
Porém, existe um grande problema nessa opção: ela não pode ser utilizada em produção porque pode acarretar em perda de dados. Por isso, é útil apenas para ambientes de desenvolvimento.
Por isso, não vamos utilizar essa opção para produção. Afinal, queremos te mostar quais são as práticas utilizadas no mercado.
Diante disso, vamos apagar a opção sincronize: true
de método createTypeOrmOptions()
e salvar o arquivo postgres.config.service.ts
.
Agora, vamos substituir essa opção pela solução mais robusta utilizada no mercado: as migrations (migrações).
Para conseguir as migrations que são um recurso específico do TypeORM, precisamos instalar a CLI do TypeORM que é a linha de comando do TypeORM.
Para isso, vamos abrir o terminal integrado do VSCode e digitar npm i -g
para instalar globalmente a versão typeORM@0.3.16
.
Contudo, ela não vai funcionar necessariamente em conjunto com o projeto Nest, mas vai ser uma parte independente. Inclusive, a API nem precisa estar rodando para utilizar os comandos da CLI.
npm i -g typeORM@0.3.16
Vamos dar um "Enter" e aguardar o comando ser executado. Após instalar a CLI do TypeORM, vamos conseguir utilizar qualquer comando precisamos criar um Data Source específico.
Já temos o Data Source no Nest, mas lembra que a CLI não faz parte do projeto? Na verdade, vamos fazer um arquivo que vai configurar um objeto muito parecido com o utilizado no postgres.config.service.ts
.
Dentro da pasta "src", vamos criar uma nova pasta chamada "db" que se refere a database. Nessa pasta, criamos um arquivo chamado data-source-cli.ts
.
Nesse arquivo, vamos colocar uma configuração específica para o TypeORM. Por isso, digitamos const
chamada dataSourceOptions
que vai ser do tipo DataSourceOptions
. Com isso, fizemos o auto import do tipo DataSourceOptions
desde typeorm
.
Vamos atribuir a essa constante o objeto que vai ser a fonte de dados. Vamos usar como base o objeto que está sendo retornado pelo método createTypeOrmOptions()
em postgres.config.service.ts
. Copiamos e colamos esse trecho na atribuição do dataSourceOptions
.
Contudo, não vamos utilizar o this.configService
porque é específico no NestJS para pegar informações do .env
, como as variáveis DB_HOST
e DB_PORT
.
Na propriedade host
, vamos substituir this.configService.get<String>()
por process.env.
para pegar diretamente da variável de ambiente. Vamos manter o DB_HOST
, porém sem aspas, pois vamos remover a string e colocar como propriedade do process.env
.
Vamos fazer o mesmo nas outras linhas. Na porta, vamos colocar process.env.DB_PORT
. Porém, para não acusar erro de atribuição do tipo string para tipo number, vamos envolver process.env
com Number()
. Isso porque as variáveis de ambiente vêm automaticamente como string.
Para fazer essa substituição mais rapidamente, você pode posicionar o cursor no início do this.configService
na linha de username
. Em seguida, aperte "Ctrl + Alt" e a seta para baixo duas vezes para também selecionar as linhas de password
e database
.
Logo, aperte "Ctrl + Shift" e seta para direita até selecionar o início da string em this.configService.get<String>()
. Agora, vamos apagar esse trecho selecionado e escrever process.env
. Por fim, basta apagar as aspas e fecha parênteses no final de cada linha.
data-source-cli.ts
:
import { DataSourceOptions } from 'typeorm';
const dataSourceOptions: DataSourceOptions = {
type: 'postgres',
host: process.env.DB_HOST,
port: Number(process.env.DB_PORT),
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
entities: [__dirname + '/../**/*.entity.{js,ts}'],
};
Para conseguir utilizar as variáveis de ambiente desse modo, também precisamos instalar o pacote dotenv
que ainda não está nesse projeto.
Para isso, abrimos o terminal integrado e escrever o comando para instalar a versão dotenv@16.0.3
npm i dotenv@16.0.3
Após instalar, temos que importar o dotenv
nesse arquivo data-source-cli.ts
. Na primeira linha, importamos dotenv/config
entre aspas.
Para finalizar as configurações do arquivo, após dataSourceOptions
, vamos escreverconst dataSource
que recebe new DataSource()
, passando o dataSourceOptions
como parâmetro. Com isso, importamos automaticamente o DataSource
também desde typeorm
.
Por fim, vamos exportar o dataSouce
como export default
. Essa é a linha que vai servir para a CLI do TypeORM, pois vai conter todas as informações do Data Source.
Essa é a maneira de criar um Data Source, passando as opções como parte do construtor. Só falta mais um detalhe que falta colocar nas opções da fonte de dados.
Após entities
, vamos dar "Enter" para escrever migrations
na próxima linha. É onde vamos indicar quais são as pastas do projeto que vão conter as migrations, seguindo um padrão similar ao usado da propriedade entities
.
Entre colchetes, escrevemos __dirname +
e, entre aspas, /migrations/*.{js,ts}
. Isso significa que vamos configurar as migrações do projeto dentro da pasta "db" onde está esse arquivo data-source-cli.ts
, mas dentro de uma pasta "migrations".
Dentro da pasta "migrations" que ainda vamos criar, vamos capturar qualquer arquivo que termine em .js
ou .ts
.
import 'dotenv/config';
import { DataSource, DataSourceOptions } from 'typeorm';
const dataSourceOptions: DataSourceOptions = {
type: 'postgres',
host: process.env.DB_HOST,
port: Number(process.env.DB_PORT),
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
entities: [__dirname + '/../**/*.entity.{js,ts}'],
migrations: [__dirname + '/migrations/*.{js,ts}'],
};
const dataSource = new DataSource(dataSourceOptions);
export default dataSource;
Após salvar esse arquivo, vamos rodar um comando específico da CLI no terminal integrado para testar se o Data Source funciona.
Normalmente, precisaríamos colocar somente typeorm
para utilizar os comandos da CLI. Porém, precisamos adicionar também -ts-node-esm
porque trabalhamos com tipos TypeScript, como o data-source-cli.ts
, e o ECMAScript module. Se não fosse esm
, escreveríamos -ts-node-commonjs
.
Em seguida, vamos adicionar a opção --dataSource
com a letra "S" maiúscula (ou somente o atalho -d
) para indicar o caminho do arquivo que acabamos de criar, começando pela raiz do projeto: src/db/data-source-cli.ts
.
Agora, podemos dar o comando que queremos executar pela CLI do TypeORM. Nesse caso, o migration:show
.
typeorm-ts-node-esm -d src/db/data-source-cli.ts migration:show
Após apertar "Enter", não acontece nada. O que é um bom sinal, já que não houve nenhum erro. Isso significa que conseguiu capturar corretamente as informações do arquivo.
Se tivesse dado algum problema, apareceria o seguinte erro no terminal:
Error during migration show
Error: Given data source file must contain export of a DataSource
O VSCode avisa que houve um erro em mostrar as migrações, pois o arquivo tinha que conter uma instância de Data Source.
Mas, se deu certo, por que não houve nenhum retorno?
Isso porque o comando migration:show
mostra todas as migrações existentes no nosso código, porém, ainda não criamos nenhuma. Contudo, nosso arquivo já está preparado para criar essas migrações - o que vamos fazer no próximo vídeo.
O curso Nest.js: lidando com migrações, relacionamentos ORM e erros em uma API possui 181 minutos de vídeos, em um total de 53 atividades. Gostou? Conheça nossos outros cursos de Node.JS em Programação, ou leia nossos artigos de Programação.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Assine o PLUS e garanta:
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você tem acesso a eventos exclusivos, grupos de estudos e mentorias com especialistas de diferentes áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Transforme a sua jornada com benefícios exclusivos e evolua ainda mais na sua carreira.
1 ano de Alura
Todos os benefícios do PRO e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.