Quando estamos construindo aplicações com bancos de dados, independente da linguagem, existem muitas, mas muitas atividades repetitivas mesmo entre sistemas completamente diferentes. Uma delas é a escrita dos comandos e consultas para fazer inserções, atualizações, etc nas tabelas do seu banco (chamadas de coleções no MongoDB) e a outra é o mapeamento das entidades e relacionamentos em objetos ou módulos da sua aplicação.
Mapear tabelas para código é um padrão muito comum independente de linguagem ou framework pois te permite programar mais próximo da regra de negócio da empresa, reduz a carga cognitiva de ficar chaveando mentalmente entre as diferentes camadas da aplicação e lhe dá muita produtividade, uma vez que, depois do mapeamento feito, atividades triviais, porém trabalhosas, como ficar escrevendo os mesmos comandos de sempre se tornam apenas simples chamadas de funções ou métodos.
Um ORM é um framework que lhe permite fazer este mapeamento de forma automática ou de forma manual, mas extremamente simplificada, como no caso do Prisma, um dos ORMs mais populares da atualidade.
No tutorial de hoje vou lhe ensinar a como construir um CRUD utilizando Node.js e MongoDB com o ORM Prisma.
Atenção: este é um tutorial para quem já conhece Node.js. Se você não conhece esta tecnologia, comece por algo mais introdutório.
Atenção 2: usaremos TypeScript aqui, em virtude do Prisma ser focado nele. Se você não sabe nada de TS, comece por este tutorial.
Caso não saiba como subir um server de MongoDb localmente para estudo, segue vídeo.
#1 – Prisma e MongoDB
O Prisma, segundo o site oficial, é um ORM da próxima geração para Node.js baseado em TypeScript, para não apenas diversos bancos SQL como também para MongoDB. Então mesmo que você não use MongoDB assim como eu, mas usa qualquer um dos bancos suportados pelo Prisma, deve conseguir adaptar este tutorial para sua realidade.
Entre suas principais características estão o suporte a transações sólidas (ACID), relacionamentos, eager e lazy loading (carregamento adiantado ou tardio), replicação de leitura e muito mais.
“Mas Luiz, se suporta vários bancos, porque você vai me ensinar com MongoDB?”
Se você nunca utilizou MongoDB antes, saiba que ele é o banco número 1 no mundo quando o assunto é banco NoSQL. Além disso você vai ver como é fácil utilizar ele, vou mostrar na prática. Mais tarde, você pode se aprofundar nele com esta série aqui do blog.
Mas primeiro, você precisa ter o MongoDB rodando na sua máquina, certo?
Para isso vamos acessar o site oficial do MongoDB e baixar o Mongo. Clique no menu superior em Products > MongoDB Community Edition > MongoDB Community Server e busque a versão mais recente para o seu sistema operacional. Baixe o arquivo ZIP e, no caso do Windows, rode o executável que extrairá os arquivos na sua pasta de Arquivos de Programas, o que é está ok para a maioria dos casos.
Você também vai precisar do Mongo Shell (mongosh), que você baixa em Products > Tools > Shell. Recomendo a versão em zip, basta extrair na pasta bin do seu MongoDB atual.
Dentro da pasta do seu projeto Node, que aqui chamei de prisma-example (crie onde você quiser), deve existir uma subpasta de nome data, crie ela agora. Nesta pasta vamos armazenar nossos dados do MongoDB.
Pelo prompt de comando, entre na subpasta bin dentro da pasta de instalação do seu MongoDB e digite (no caso de Mac e Linux, coloque um ./ antes do mongod e ajuste o caminho da pasta data de acordo):
1 2 3 |
mongod --dbpath c:\prisma-example\data --replSet rs0 |
Isso irá iniciar o servidor do Mongo em modo de replicat set, algo exigido pelo Prisma. Ele vai ficar dando erro e tentando encontrar os pares dele, mas como não vamos subir, você precisa acessar o mongosh em outro terminal (primeiro comando) e rodar o comando na sequência para dizer que é apenas um nó sozinho.
1 2 3 4 5 |
mongosh rs.initiate({_id: 'rs0', members: [{_id: 0, host: 'localhost:27017'}]}) |
Se tudo der certo, o terminal do servidor de MongoDB vai parar de dar erro agora e ficar aguardando conexões.
Aproveite que está com o Mongo Shell aberto e vamos nos conectar no banco que vamos usar e inserir alguns dados de exemplo, com os comandos abaixo (execute um de cada vez).
1 2 3 4 5 6 7 8 |
use workshoptdc db.customers.insertOne({ "name" : "Luiz Duarte", "age" : 35 }) |
Uma de minhas coisas favoritas sobre MongoDB é que ele usa JSON como estrutura de dados, o que significa curva de aprendizagem zero para quem já conhece o padrão. O atributo _id é a chave primária no MongoDB e foi omitido, pois o próprio Mongo gera um id pra você. Adicione alguns registros para ter uma base mínima para o desenvolvimento e agora sim, vamos interagir de verdade com o Node.js + MongoDB.
#2 – Estruturando o Projeto
Agora indo ao que interessa, crie uma aplicação Node.js na sua máquina com npm init e instale as dependências que vamos precisar.
1 2 3 |
npm i -D typescript ts-node prisma |
Agora vamos inicializar o TypeScript no projeto com o comando abaixo.
1 2 3 |
npx tsc --init |
Crie um arquivo index.ts com um console.log qualquer e vamos ajustar o package.json para que inicialize o projeto com o TS Node e este arquivo que recém criamos.
1 2 3 4 5 |
"scripts": { "start": "ts-node index" }, |
Agora vamos falar de Prisma. Rode o comando abaixo para inicializar o Prisma neste projeto.
1 2 3 |
npx prisma init |
Isso vai criar uma pasta prisma com um schema.prisma (falaremos dele em breve) e um arquivo .env na raiz do projeto. Abra esse .env e preencha a variável DATABASE_URL com a connection string do seu MongoDB. Abaixo a minha, como exemplo (não use localhost, mas sim 127.0.0.1 se for o caso).
1 2 3 |
DATABASE_URL=mongodb://127.0.0.1:27017/workshoptdc |
Agora abra seu prisma/schema.prisma e mude o provider para mongodb.
1 2 3 4 5 6 7 8 9 10 |
generator client { provider = "prisma-client-js" } datasource db { provider = "mongodb" url = env("DATABASE_URL") } |
Agora que configuramos o Prisma para conhecer onde está nosso banco, é hora de criar os nossos schemas, sendo que vou criar apenas um como exemplo, de clientes (customers).
1 2 3 4 5 6 7 |
model customers { id String @id @default(auto()) @map("_id") @db.ObjectId name String age Int } |
Aqui estamos dizendo que o id do nosso objeto deve ser mapeado para um campo _id no banco de dados que é do tipo ObjectId (nativo do MongoDB) gerado automaticamente. Além disso estamos dizemos que nosso schema possui um campo name (string) e um campo age (int). Mude livremente estes campos conforme a sua necessidade.
E por fim, agora você deve rodar o comando abaixo para gerar o Prisma Client.
1 2 3 |
npx prisma generate |
O Prisma Client é uma API gerada de maneira personalizada para o seu banco de dados e é com ele que vamos usar o Prisma de fato no projeto.
Importante sinalizar que toda vez que mexer no schema.prisma, deve fazer um novo generate.
Com isso terminamos a etapa inicial de configuração do projeto, podemos começar a programá-lo agora.
#3 – Padrão Repository
O próximo passo é criar o módulo que vai usar o Prisma para fazer as leituras e escritas no banco de dados. Para isso, é comum usarmos o padrão Repository que nada mais é do que um módulo que encapsula todas as funções de manipulação de uma coleção do banco de dados.
Crie um arquivo customersRepository.ts na raiz do seu projeto e dentro dela inclua o seguinte código.
1 2 3 4 5 6 7 8 9 10 |
import { PrismaClient } from '@prisma/client'; const prisma = new PrismaClient(); async function connect() { await prisma.$connect(); } connect(); |
Esse código importa o Prisma Client e inicializa ele em uma variável local. Então eu defini uma função de conexão que abrir essa ponte de comunicação com o banco e já chamo a função imediatamente, garantindo que teremos uma conexão aberta e funcional quando precisarmos. Não se preocupe com a desconexão, ela é feita automaticamente quando o processo do Node se encerrar.
Agora precisamos criar as funções de manipulação do banco, uma a uma. Para isso, basta usarmos o objeto prisma, que já está conectado, e chamar as funções correspondentes ao que queremos fazer, iniciadas em find, create, update e delete, o que cobre todo o CRUD. Começando pelos finds:
1 2 3 4 5 6 7 8 9 10 11 |
export function getCustomers() { return prisma.customers.findMany(); } export function getCustomer(id: string) { return prisma.customers.findUnique({ where: { id } }) } |
Ambas são bem semelhantes, com a diferença que a segunda usa um filtro pois queremos apenas um usuário, aquele cujo id seja o passado por parâmetro. Repare que na primeira função usei findMany, pois eram vários clientes a serem encontrados, enquanto que na segunda usei findUnique, pois é apenas um e quero aproveitar o índice da chave primária (maior performance).
Agora vamos escrever as funções de escrita:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
export function addCustomer(newCustomer: any) { return prisma.customers.create({ data: newCustomer }); } export function updateCustomer(id: string, newData: any) { return prisma.customers.update({ where: { id }, data: newData }) } export async function deleteCustomer(id: string) { return prisma.customers.delete({ where: { id } }) } |
Na addCustomer, usamos a função create que espera uma propriedade data com os dados a serem salvos no banco. Opcionalmente esta função espera uma propriedade select que pode mudar o retorno da mesma (por padrão ela retorna o último inserido).
Na updateCustomer, usamos a função update, informando no where o filtro para selecionar os registros que serão atualizados, seguido da propriedade data informando os dados que serão atualizados.
E por fim, na deleteCustomer usamos a função delete, que apenas espera um where com o filtro a ser usado para selecionar os registros a serem excluídos.
Com isso, você tem um exemplo de CRUD completo com Prisma ORM implementado no padrão repository. Se você tiver outras coleções no seu banco, crie um módulo repository para cada uma.
#4 – Aplicação de Teste
O próximo passo é utilizar estes módulos que criamos de fato na nossa aplicação.
Como mencionei antes, o Prisma vai fazer a gestão não apenas das conexões com o banco, como a criação das coleções necessárias, se elas ainda não existirem.
Então em nosso index.ts, basta importamos o repository e chamarmos as funções que quisermos testar. Exemplo de teste de escrita:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import { addCustomer } from "./customersRepository"; async function start() { const result = await addCustomer({ name: "Luiz", age: 35 }) console.log(result); } start(); |
O código não tem nada demais, eu carrego o repository da coleção que quero manipular e chamo a função apropriada, imprimindo no console o resultado ou o erro.
E com isso finalizamos este tutorial em que você aprendeu como fazer um CRUD bem simples usando TypeScript, Prisma e MongoDB.
Tenho certeza que juntando com outros conhecimentos de Node.js vai lhe permitir construir aplicações de verdade, como a webapi que ensino neste tutorial, com NestJS. Caso esteja trabalhando com monorepo, acho que esse outro tutorial vai te ajudar.
Um abraço e até a próxima!
Olá, tudo bem?
O que você achou deste conteúdo? Conte nos comentários.