Como compartilhar Prisma Schema/Client em Monorepo

Node.js

Como compartilhar Prisma Schema/Client em Monorepo

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

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

Atualizado em 30/12/23!

Quando estamos trabalhando em diversos projetos com Node.js, seja qual for a natureza deles, é muito comum haver a necessidade de compartilhamento de módulos para evitar duplicação de código e facilitar a manutenção futura. Um desses módulos muito comuns de precisarem de compartilhamento são os de acesso a dados, seja usando ORMs ou não. No entanto, dependendo das tecnologias e arquitetura escolhidas isso pode se tornar um desafio bem grande, como por exemplo no caso de compartilhar o schema ou o client do ORM Prisma.

O Prisma usa um CLI para gerar seu  client a partir de um arquivo schema.prisma. Esse client é gerado baseando-se nas configurações definidas no projeto e uma vez gerado pode ser usado para manipulação do banco de dados pela aplicação. No entanto, por não ser o schema.prisma um módulo JS tradicional, não conseguimos compartilhar ele entre os diferentes projetos usando métodos convencionais que falei neste outro post. Mas calma, que tem solução.

Não vou falar aqui de publicação no NPM e nem no GitHub, que seriam duas alternativas bem viáveis, muito profissionais (principalmente NPM por permitir versionamento de módulos) e  também e as mais fáceis de fazer, mas que no entanto exigem que você tenha os seus módulos compartilhados como públicos (free) ou como privados, mas pagando para o NPM. Também não vou entrar em alternativas mais complexas de fazer gestão de monorepo usando Lerna ou Rush. Vou propor uma abordagem mais light e que pode resolver o seu problema, como resolveu o meu.

Ah, entendo aqui que você está usando uma abordagem monorepo para o versionamento do seu projeto, ou seja, guarda todos eles em um mesmo repositório.

Curso Node.js e MongoDB

#1 – Estruturando a Commons

Para o exemplo prático, vou criar um monorepo chamado prisma-monorepo com dois projetos dentro:

  • _commons_: projeto comum aos demais do monorepo, onde teremos os módulos compartilhados;
  • application: projeto onde está uma aplicação que precisa usar os módulos da commons;

Vamos começar preparando a commons, a começar pela inicialização:

Depois instale as dependências que vamos precisar:

Inicialize o TypeScript:

E inicialize o Prisma:

Será gerado um .env, mas você pode ignorá-lo pois cada projeto vai ter o seu. Vá no seu prisma/schema.prisma e ajuste de acordo com seu banco de dados normalmente. Apenas atenção a esta configuração adicional de output, que diz onde deve ser salvo o Prisma Client que vamos gerar:

Após a configuração do schema.prisma, rode o comando abaixo para gerar o Prisma Client:

Isso vai fazer com que a pasta data no projeto commons fique populada e pronta para ser usada como Prisma Client.

Curso Beholder
Curso Beholder

#2 – Aplicação de Exemplo

Agora vamos para a pasta application, onde teremos uma aplicação de exemplo que vai usar o Prisma Client da commons.

Vamos começar inicializando este segundo projeto:

E adicionando as dependências que vamos precisar:

Agora crie um arquivo .env na raiz desse projeto de aplicação e inclua nele a seguinte variável, preenchendo-a com a sua connection string correta (no meu exemplo estou usando MongoDB).

Na sequência, inicialize o TypeScript nesse projeto:

E ajuste o package.json para inicializar o projeto com TS Node:

E por fim, crie o arquivo index.ts que neste exemplo vai usar o Prisma Client da commons:

Repare que carreguei o .env antes do Prisma Client, pois internamente ele depende da variável DATABASE_URL. Repare também que importei o PrismaClient a partir da pasta data da commons. Depois, não tem nenhum código específico para este exemplo, sendo tudo de acordo com o seu schema e uso normal do Prisma.

O que você tem de tomar cuidado com esta abordagem é apenas com o fato de que toda vez que mudar seu schema.prisma, precisará mandar gerá-lo de novo e retestar todos projetos que usam a pasta data da commons. Nada diferente do que você já teria de cuidar com o Prisma em single project, mas que não custa eu reforçar.

#3 – Mapeando o Caminho

Dependendo da estrutura de pastas que você tiver, como no meu caso onde costumo ter uma pasta “data” dentro da _commons_ com os arquivos do Prisma Client dentro, pode ser que o caminho fique um tanto comprido nos seus imports do projeto. Para resolver isso podemos remapear o caminho até seu projeto commons, sendo o primeiro passo instalar um novo pacote.

Depois, vá no seu tsconfig.json e ajuste quatro configurações:

A primeira configuração é apenas a definição da pasta onde ficarão os .JS compilados a partir dos .TS, provavelmente você já tem esta configuração na sua aplicação TypeScript.

A segunda configuração diz a URL base para resolução de caminhos de arquivos e é pré-requisito para a segunda configuração, só por isso defini ela (usei . que quer dizer “pasta atual”).

A terceira configuração define aliases (apelidos) para os caminhos relativos que queremos mapear os nossos módulos compartilhados. Aqui estou dizendo que todos módulos que começarem com commons/ na verdade devem ser resolvidos para ../_commons_/ (ou seja, estão um nível acima em relação a URL base).

E por último, para que não dê erro em tempo de desenvolvimento, a configuração rootDirs diz pro TypeScript onde estão todos os arquivos .TS necessários para a correta interpretação (em tempo de dev) e compilação do projeto como um todo. Assim, além de olhar na pasta raiz do projeto, o TS vai olhar na pasta _commons_ também.

#4 – Cuidados no NestJS

Se você estiver trabalhando com NestJS tem de tomar alguns cuidados adicionais com esta abordagem proposta no artigo. Isso porque ele vai se perder pois na pasta do build (que eu costumo chamar de pasta “dist”) vão constar os projetos do seu backend e da commons, em pastas separadas, além do que na compilação não serão copiados os arquivos do seu Prisma Client (pasta “data”).

Sendo assim, você precisa ajustar o seu script de start para que ele procure o arquivo main na pasta certa e também ajustar o script de build para que ele copie também os arquivos de commons/data, a fim de que funcione o Prisma Client na versão compilada.

Primeiro, vamos ajustar o nest-cli.json para que ele procure o arquivo main no lugar certo (entryFile) e para que não exclua a pasta dist a cada compilação.

Depois, vamos no package.json do projeto Nest, ajustar a seção de scripts (exibo abaixo apenas scripts que sofreram mudanças).

O ponto mais importante aqui é o novo script copy-files, que faz uma cópia de todo o conteúdo da pasta “data” da commons para a dist (as flags -rf servem para agir recursivamente e sobrescrevendo arquivos existentes, enquanto que o ‘yes’ confirma essa ação), garantindo que o Prisma Client esteja onde ele deve estar. Agora temos de chamar esse script copy-files com “npm run copy-files” sempre que quizermos que os arquivos sejam copiados. Opcionalmente, você pode incluir o “npx prisma generate” aqui também, para garantir que estará com a última versão dos schemas e da API.

Depois, usamos esse script de cópia de arquivos nos demais scripts em que ele for necessário, garantindo que ele seja chamado no momento certo.

Um último ponto que vale ser citado é o ajuste que fiz no script start:prod para que o arquivo main seja encontrado na nova estrutura de pastas que vai ter dentro da dist (meu projeto de backend está em uma pasta chamada “backend”).

Espero ter ajudado!

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 *