Tutorial de CRUD com Node.js, Sequelize e PostgreSQL - Parte 2

Node.js

Tutorial de CRUD com Node.js, Sequelize e PostgreSQL - Parte 2

Luiz Duarte
Escrito por Luiz Duarte em 13/09/2021
Junte-se a mais de 34 mil devs

Entre para minha lista e receba conteúdos exclusivos e com prioridade

Recentemente eu escrevi um tutorial bem básico de como usar Node.js com PostgreSQL, usando o ORM Sequelize como intermediador, o que nos permite trabalhar apenas com objetos, sem a necessidade de escrever SQL, entre outros benefícios que falei na primeira parte.

A ideia desta segunda parte é justamente entrar em alguns aspectos mais avançados deste popular ORM escrito em JavaScript, como por exemplo o mapeamento de relacionamentos entre tabelas/objetos.

Vamos seguir o desenvolvimento a partir de onde paramos no tutorial anterior. Se preferir, você pode assistir ao vídeo abaixo ao invés de ler este tutorial.

Atenção: este é um post intermediário. Não comece querendo aprender Node.js com ele, busque posts mais introdutórios aqui no blog.

Fabricantes

Para isso, vamos começar criando um modelo de fabricantes de produtos. A ideia é que cada produto vai poder ter apenas um fabricante, relacionamento representado por uma chave estrangeira de idFabricante na tabela de produtos. Um clássico 1-1 (cada produto tem apenas um fabricante) ou 1-N (cada fabricante pode ter muitos produtos), dependendo do seu ponto de vista.

O nosso modelo de fabricante ficará em um fabricante.js, com o conteúdo abaixo.

Não vou entrar nos detalhes pois é “mais do mesmo” em relação à primeira parte, onde definimos o modelo de Produto. Apenas reforço que não há necessidade de configurar uma chave estrangeira manualmente no model de produto, mas você pode fazê-lo se assim desejar.

Optei também por criar uma pasta models e colocar tanto produto quanto fabricante dentro, para ficar melhor organizado.

Agora, vamos criar um novo arquivo, que vou chamar de index2.js, onde, antes do sync do Sequelize com o banco de dados, eu vou definir o relacionamento, se ele é ou não é uma contraint e qual o nome da chave estrangeira que eu quero que seja criada na tabela produtos (opcional, ele gera esse nome automaticamente).

Note que usei a opção force no sync, para que ele recrie as tabelas, uma vez que estou fazendo uma mudança sem retrocompatibilidade. Outra alternativa seria usar migrations, que falaremos em post futuro.

Para cadastrar um Fabricante e depois usá-lo em um produto, você pode fazê-lo da seguinte forma (logo depois do sync do Sequelize).

Obtendo o Fabricante

Agora, com as tabelas e relacionamentos criados, é hora de usar esse relacionamento. Podemos fazê-lo de duas maneiras: carregando o fabricante de um produto ou carregando os produtos de um fabricante.

Não obstante, você pode decidir fazer eager loading (carregamento antecipado) ou lazy loading (carregamento tardio) e aqui valem duas explicações rápidas.

Eager Loading é você trazer como resultado de uma consulta, também os dados de subtabelas relacionadas. Ou seja, você faz uma consulta mais pesada uma única vez, trazendo todos os dados que vai querer. Isso é útil quando tem certeza de tudo que vai precisar.

Lazy Loading é você trazer os dados de subtabelas relacionadas somente quando necessário, depois que já trouxe os dados da tabela principal. Ou seja, você faz uma consulta leve, só com os dados básicos e depois, se precisar, volta no banco para pegar mais. Isso é útil quando provavelmente você não vai precisar de mais dados.

Se você usar errado, o impacto será idas no banco desnecessárias e consequentemente maior lentidão. Nada significativo em uma pequena aplicação, mas importante para sistemas de maior porte.

Assim, quando retornamos um produto, podemos pegar o seu fabricante junto, na consulta de produto, ou depois, quando precisarmos, mostro os dois exemplos abaixo. Começando com Eager Loading:

Note a opção include passada no findByPk, ela vai alterar o SQL gerado pelo Sequelize para incluir um JOIN com a tabela de fabricante e retornar os seus dados juntos.

Assim, quando eu chamo a função getFabricante (gerada automaticamente pelo Sequelize), ele já está com o objeto fabricante pronto e é instantâneo. Ou seja, tempo maior no findByPk, tempo menor no getFabricante.

Agora, usando Lazy Loading:

Note que só tirei o include do findByPk. Assim, quando eu chamar o getFabricante, será feita nova consulta no banco de dados para baixar os dados do fabricante e montar o objeto. Ou seja, menor tempo no findByPk, tempo maior no getFabricante.

Produtos de um fabricante

Mas eu mencionei que podíamos usar deste mesmo relacionamento para obter os produtos de um fabricante, certo? A clássica relação 1-N.

Para isso, precisamos primeiro dizer ao Sequelize que este caminho também existe, da seguinte forma.

Esse código pode ficar antes ou depois do belongsTo que usamos antes (e antes do sync) e permitirá termos um getProdutos no objeto do fabricante, como abaixo.

Note que usei Eager Loading aqui também, o que em relações 1-N tem de ser feito com cuidado, pois a segunda tabela do JOIN pode ter muitos registros e isso dar uma travada na sua aplicação.

Curso Node.js e MongoDB

Categorias do produto

Mas e se tivermos um relacionamento N-N, o que pela notação formal nós teríamos de criar uma “tabela meio” com duas chaves estrangeiras?

Por exemplo, categorias de um produto? Como fazer isso com Sequelize?

Vamos começar criando nosso modelo de categoria:

Agora, temos de criar o modelo que representará a nossa “tabela meio”, que chamarei aqui de CategoriaProduto.

Coloquei apenas o id pois o Sequelize vai gerar as chaves estrangeiras pra gente, depois você pode pensar em coisas mais elaboradas que fizerem sentido para o problema. Se você criar as chaves estrangeiras aqui, o Sequelize vai respeitá-las se você configurá-lo corretamente também.

Depois, vamos adicioná-la nos requires do nosso index2.js.

E agora, vamos criar o relacionamento de produto com categoria, de maneira N-N passando pela tabela categoriaProduto. O código abaixo deve estar antes do sync, junto dos demais relacionamentos.

Aqui eu já adicionei nas duas direções, caso queiramos pegar produtos pela categoria ou as categorias de um produto. Em ambos os casos, usa-se o belongsToMany, que exige uma propriedade through nas opções do relacionamento, onde indicamos o model da tabela meio, além das opções normais para relacionamentos que já vimos antes.

O resultado disso é que teremos funções de “categorias ” no objeto produto e funções de “produtos” no objeto categoria, como abaixo, onde busco um produto, uma categoria, e depois defino que aquele produto deve pertencer àquela categoria.

Outro exemplo possível, seria fazendo o caminho inverso.

Note que em ambos os casos, o set espera receber um array de elementos, mesmo que venhamos a passar apenas um. Isso porque ele é um update completo. Querendo adicionar apenas um, use add ao invés de set.

Para obter as categorias de um produto ou os produtos de uma categoria, basta usar os equivalentes gets e aqui tem um truque bem interessante para quem prefere ou precisa de Eager Loading. Por padrão, você precisaria pegar as CategoriaProduto primeiro e depois, a partir delas, pegar as categorias, certo?

Mas tem um truque do Sequelize chamado Super Many-to-Many Relationships onde, se você tiver os relacionamentos mapeados de Produto para CategoriaProduto e de CategoriaProduto para Categoria, você consegue fazer com que Produto acesse Categoria e vice-versa como se eles fossem diretamente conectados!

Sendo assim, adicione os seguintes relacionamentos:

E agora você pode consultar as categorias de produto como se fosse um One-To-Many (hasMany).

Legal, não?!

Então é isso por hoje, espero que tenha gostado de mais esse tutorial de Node.js com PostgreSQL usando Sequelize.

Quer aprender a fazer uma aplicação web completa usando Sequelize e seu banco favorito? Confira este tutorial (inclui vídeo).

Quer aprender a escrever testes unitários mockando o Sequelize? Leia este tutorial.

Até a próxima!

Quer aprender a construir uma aplicação completa em Node.js com Sequelize e banco SQL? Dá uma olhada no meu curso abaixo!

Curso FullStack

Olá, tudo bem?

O que você achou deste conteúdo? Conte nos comentários.

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *