Pular para o conteúdo
Cursos

DevClub

LógicaFront-endBack-endMobile

IA Club

IA na prática
Estudar programaçãoEstudar IA
LiçãoIntermediáriocódigo testado

Prisma com NestJS e PostgreSQL: integração completa

Integre Prisma 7.9.1 ao NestJS com driver adapter, migration e PostgreSQL 18.6, usando um PrismaService injetável e consultas comprovadas no banco.

Rodolfo Mori6 min de leitura

Integrar Prisma 7 ao NestJS significa transformar o Prisma Client numa dependência gerenciada pelo framework e entregar a ele um driver adapter. Nesta lição, você vai subir PostgreSQL 18.6, criar uma migration e comprovar que a API de mentorias persiste e consulta dados reais.

O guia de Prisma detalha schema, Client e migrations, e o guia de PostgreSQL explica tabelas, constraints e SQL. Aqui a gente concentra a atenção na costura das duas ferramentas dentro do Nest.

Um terminal compartilhado ligado pelo cabo correto

Imagine um arquivo com um único terminal de consulta usado por vários setores. Cada setor pede uma operação ao terminal; ninguém instala uma conexão escondida na própria mesa. Um cabo compatível liga o terminal ao sistema do arquivo, e o terminal é desligado corretamente quando o prédio fecha.

No mapa técnico, PrismaService é o terminal compartilhado, os services de domínio são os setores, @prisma/adapter-pg é o cabo para o driver pg, e PostgreSQL é o arquivo persistente. O hook onModuleDestroy encerra o Client no ciclo de vida do Nest.

A comparação não quer dizer que exista exatamente uma conexão TCP. O driver administra um pool, e o banco pode atender várias operações. “Um terminal” quer dizer uma instância gerenciada do Client por aplicação, não uma única consulta por vez.

Suba PostgreSQL 18.6 sem publicar senha

Crie uma senha efêmera numa variável, um volume nomeado e um container acessível apenas pelo loopback da máquina:

bash
export PG_APP_PASSWORD="$(openssl rand -hex 24)"

docker volume create nestjs-prisma-pg

docker run -d --name nestjs-prisma-pg \
  -e POSTGRES_USER=nest \
  -e POSTGRES_PASSWORD="$PG_APP_PASSWORD" \
  -e POSTGRES_DB=mentorias \
  -p 127.0.0.1:55440:5432 \
  -v nestjs-prisma-pg:/var/lib/postgresql \
  postgres:18.6-alpine

O destino do volume é /var/lib/postgresql. A imagem oficial mudou a organização na linha 18; usar /var/lib/postgresql/data como nos tutoriais antigos faz o container 18 abortar para proteger seus dados.

No teste, a imagem baixada teve digest sha256:d3e1620b530c944afa6e887d22eb899824da68e19c52024bf98f5220c88a65b2. Confirme saúde e versão sem revelar a senha:

bash
docker exec nestjs-prisma-pg pg_isready -U nest -d mentorias
docker exec nestjs-prisma-pg psql -U nest -d mentorias \
  -tAc 'SHOW server_version;'
/var/run/postgresql:5432 - accepting connections 18.6

O bind 127.0.0.1 evita expor a porta em todas as interfaces. Isso protege o laboratório de acessos acidentais; produção ainda exige rede privada, política de acesso, backup e credenciais gerenciadas.

Instale Prisma 7 e o adapter PostgreSQL

Na raiz de agenda-mentorias, instale as versões testadas:

bash
npm install @prisma/client@7.9.1 @prisma/adapter-pg@7.9.1 \
  pg@8.16.3 dotenv@17.2.2

npm install --save-dev prisma@7.9.1 @types/pg@8.15.5

npx prisma --version
prisma : 7.9.1 @prisma/client : 7.9.1 Operating System : darwin Architecture : arm64 Node.js : v24.16.0 TypeScript : 5.9.3 Query Compiler : enabled

Sistema e arquitetura variam na sua máquina. As linhas decisivas são as versões dos pacotes e do Node. Em 22 de agosto de 2026, 7.9.1 era a versão GA marcada como latest; a linha 8 estava em release candidate e não entra nesta aula.

Inicialize os arquivos do Prisma

Rode o gerador inicial:

bash
npx prisma init \
  --datasource-provider postgresql \
  --output ../src/generated/prisma
Initialized Prisma in your project

prisma/ schema.prisma prisma.config.ts .env .gitignore

Garanta que .env esteja no .gitignore. Depois escreva a URL usando a variável temporária; o comando não imprime a senha:

bash
printf 'DATABASE_URL="postgresql://nest:%s@127.0.0.1:55440/mentorias?schema=public"\n' \
  "$PG_APP_PASSWORD" > .env

unset PG_APP_PASSWORD

O arquivo local contém uma credencial e não deve ser commitado. Em produção, o valor vem da plataforma ou de um cofre de segredos, não de um arquivo dentro da imagem.

Separe a configuração da CLI da conexão de runtime

prisma.config.ts informa à CLI onde estão schema, migrations e URL:

ts
import "dotenv/config";
import { defineConfig } from "prisma/config";

export default defineConfig({
  schema: "prisma/schema.prisma",
  migrations: {
    path: "prisma/migrations",
  },
  datasource: {
    url: process.env["DATABASE_URL"],
  },
});

Comandos como prisma migrate dev usam essa configuração. A aplicação Nest também lê DATABASE_URL, mas entrega a string ao driver adapter. São dois momentos diferentes usando a mesma conexão no laboratório.

Modele a tabela de mentorias

Edite prisma/schema.prisma:

text
generator client {
  provider     = "prisma-client"
  output       = "../src/generated/prisma"
  moduleFormat = "cjs"
}

datasource db {
  provider = "postgresql"
}

model Mentoria {
  id       Int      @id @default(autoincrement())
  titulo   String
  vagas    Int
  criadaEm DateTime @default(now()) @map("criada_em")

  @@map("mentorias")
}

O scaffold atual do Nest compila este projeto como CommonJS, por isso o Client foi gerado com moduleFormat = "cjs". @map escolhe o nome da coluna no banco e @@map escolhe o nome da tabela; no TypeScript, a gente mantém camelCase.

O schema descreve tipos e chave. Limites como vagas positivas ainda merecem DTO e, quando são invariantes do dado, uma constraint SQL revisada. ORM não substitui as garantias do banco.

Crie a migration e gere o Client

Valide, migre e gere explicitamente:

bash
npx prisma validate
npx prisma migrate dev --name criar_mentorias
npx prisma generate
The schema at prisma/schema.prisma is valid 🚀

Applying migration 20260823015430_criar_mentorias The following migration(s) have been created and applied from new schema changes: prisma/migrations/20260823015430_criar_mentorias/migration.sql

Generated Prisma Client (7.9.1) to ./src/generated/prisma

O timestamp da sua pasta será outro. O importante é guardar migration.sql no Git e conseguir aplicar o mesmo histórico nos demais ambientes. Prisma 7 não gera o Client automaticamente depois de migrate dev; rode os dois comandos.

O SQL real criado no teste foi:

sql
CREATE TABLE "mentorias" (
  "id" SERIAL NOT NULL,
  "titulo" TEXT NOT NULL,
  "vagas" INTEGER NOT NULL,
  "criada_em" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT "mentorias_pkey" PRIMARY KEY ("id")
);

Migration é a ponte revisável entre mudança de modelo e estrutura persistida. No desenvolvimento use migrate dev; no pipeline de produção use prisma migrate deploy.

Crie PrismaService com adapter obrigatório

Crie src/prisma/prisma.service.ts:

ts
import { Injectable, OnModuleDestroy } from "@nestjs/common";
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaClient } from "../generated/prisma/client";

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleDestroy {
  constructor() {
    const connectionString = process.env.DATABASE_URL;

    if (!connectionString) {
      throw new Error("DATABASE_URL não definida");
    }

    super({ adapter: new PrismaPg({ connectionString }) });
  }

  async onModuleDestroy() {
    await this.$disconnect();
  }
}

No Prisma 7, new PrismaClient() vazio não é a configuração atual. PrismaPg recebe a URL e cria a ponte para o driver pg. O provider valida a variável no bootstrap e expõe os métodos gerados por herança.

Agora crie src/prisma/prisma.module.ts:

ts
import { Module } from "@nestjs/common";
import { PrismaService } from "./prisma.service";

@Module({
  providers: [PrismaService],
  exports: [PrismaService],
})
export class PrismaModule {}

Importe PrismaModule em cada módulo que usa o Client. Eu prefiro essa ligação explícita a marcar tudo como global: olhando imports, a pessoa já sabe que a funcionalidade depende do banco.

Troque a memória por operações do Prisma

Atualize MentoriasModule:

ts
@Module({
  imports: [AuthModule, PrismaModule],
  controllers: [MentoriasController],
  providers: [MentoriasService],
})
export class MentoriasModule {}

E troque o array do service pelo Client injetado:

ts
import { Injectable } from "@nestjs/common";
import { PrismaService } from "../prisma/prisma.service";

@Injectable()
export class MentoriasService {
  constructor(private readonly prisma: PrismaService) {}

  listar() {
    return this.prisma.mentoria.findMany({ orderBy: { id: "asc" } });
  }

  criar(titulo: string, vagas: number) {
    return this.prisma.mentoria.create({ data: { titulo, vagas } });
  }
}

O controller não mudou. Esse é o ganho da fronteira: HTTP continua delegando ao service, enquanto o service troca a infraestrutura interna. A lição de guards e autenticação já deixou o POST protegido.

Execute a API e comprove persistência

Carregue o .env, gere um segredo JWT efêmero e inicie:

bash
set -a
source .env
set +a
JWT_SECRET="$(openssl rand -hex 32)" npm run start

Faça login sem imprimir o token e crie duas mentorias:

bash
LOGIN_JSON=$(curl -s -X POST http://localhost:3000/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"usuario":"mentor","senha":"senha-da-aula"}')
SESSION_TOKEN=$(printf '%s\n' "$LOGIN_JSON" | jq -r .access_token)

curl -s -X POST http://localhost:3000/mentorias \
  -H "Authorization: Bearer ${SESSION_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d '{"titulo":"NestJS com Prisma","vagas":10}'

curl -s -X POST http://localhost:3000/mentorias \
  -H "Authorization: Bearer ${SESSION_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d '{"titulo":"PostgreSQL na prática","vagas":"7"}'
{"id":1,"titulo":"NestJS com Prisma","vagas":10,"criadaEm":"2026-08-23T01:57:11.321Z"} {"id":2,"titulo":"PostgreSQL na prática","vagas":7,"criadaEm":"2026-08-23T01:57:11.335Z"}

Os timestamps são a saída real do teste e serão diferentes na sua execução. O segundo vagas chegou como texto e o DTO o transformou antes de Prisma receber o número.

Consulte diretamente o banco, fora da API:

bash
docker exec nestjs-prisma-pg psql -U nest -d mentorias \
  -c 'SELECT id, titulo, vagas FROM mentorias ORDER BY id;'
id | titulo | vagas ----+-----------------------+------- 1 | NestJS com Prisma | 10 2 | PostgreSQL na prática | 7 (2 rows)

Essa consulta é a prova que o dado atravessou controller, service, adapter e PostgreSQL. Reiniciar a API não apaga as linhas porque o volume preserva o banco.

Erro real: Prisma 7 sem driver adapter

Se alguém copiar uma inicialização antiga:

ts
import { PrismaClient } from "./generated/prisma/client";

const prisma = new PrismaClient();

O tipo já pede opções. Ao executar o JavaScript gerado sem elas, Prisma 7.9.1 produziu:

PrismaClientInitializationError: PrismaClient was instantiated without any options. A driver adapter is required to connect to your database.

Pass a driver adapter to the PrismaClient constructor.

A correção não é esconder o erro com cast. Instale o adapter do banco e passe new PrismaPg({ connectionString }) em super({ adapter }), como no provider.

Outro erro comum é o banco estar fora do ar. Uma porta controladamente errada reproduziu:

bash
DATABASE_URL='postgresql://nest:senha-local@127.0.0.1:59999/mentorias' \
  npx prisma migrate status
Error: P1001: Can't reach database server at `127.0.0.1:59999`

Please make sure your database server is running at 127.0.0.1:59999.

P1001 aponta para alcance de rede, não para campo do DTO. Confira docker ps, pg_isready, host e porta. Nunca imprima a URL completa de produção ao investigar.

Missão: prove que a API usa o banco

Adicione o campo opcional descricao String? ao modelo. Crie uma migration chamada descricao_mentoria, gere o Client, atualize DTO e service e envie uma mentoria com descrição.

bash
npx prisma migrate dev --name descricao_mentoria
npx prisma generate
npm run build
docker exec nestjs-prisma-pg psql -U nest -d mentorias \
  -c 'SELECT titulo, descricao FROM mentorias ORDER BY id DESC LIMIT 1;'

O critério de sucesso é: migration nova versionada, build sem erro, POST com 201 e o mesmo texto visível no SELECT. Depois rode a aplicação novamente sem migrate dev; ela deve apenas usar a estrutura já aplicada.

Quando terminar o laboratório e aceitar perder os dados, remova somente os recursos nomeados nesta aula:

bash
docker rm -f nestjs-prisma-pg
docker volume rm nestjs-prisma-pg

O segundo comando apaga as linhas do laboratório de forma definitiva. Não use o nome de um volume real. Para aprofundar a ferramenta sem o framework, siga instalação do Prisma com PostgreSQL; para revisar todas as camadas, volte ao guia de NestJS.

  • nestjs
  • prisma 7
  • postgresql
  • driver adapter
  • banco de dados
  • typescript

Perguntas frequentes

Prisma 7 precisa de adapter no NestJS?
Sim. No runtime atual, PrismaClient precisa receber um driver adapter. Para PostgreSQL, instale @prisma/adapter-pg e passe uma instância no construtor.
Por que criar PrismaService?
O provider centraliza criação, injeção e encerramento do PrismaClient. Os services de domínio recebem essa dependência em vez de espalhar novos clients e pools pelo projeto.
Onde fica DATABASE_URL no Prisma 7?
A CLI lê a URL por prisma.config.ts. A aplicação também lê a variável em runtime e a entrega ao adapter; o valor não deve ser versionado.
Posso usar migrate dev em produção?
Não. Use migrate dev para criar migrations no desenvolvimento. Em staging e produção, revise o SQL e aplique o histórico com prisma migrate deploy.
Qual é o mount correto da imagem PostgreSQL 18?
Na imagem oficial 18, monte o volume em /var/lib/postgresql. O caminho /var/lib/postgresql/data usado em exemplos antigos faz o container 18 recusar a configuração de persistência.

Dúvidas e comentários

Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.

Todo o código deste artigo foi executado em Node 24.16.0, NestJS 11.2.1, TypeScript 5.9.3, Prisma 7.9.1, @prisma/adapter-pg 7.9.1, PostgreSQL 18.6 em postgres:18.6-alpine e Docker 29.5.3, e as saídas exibidas são as reais — como produzimos este conteúdo.

Fontes consultadas

  1. NestJS — receita oficial com Prisma — docs.nestjs.com
  2. Prisma ORM — driver adapters — prisma.io
  3. Prisma ORM — geração do Client — prisma.io
  4. Prisma Migrate — desenvolvimento e produção — prisma.io
  5. PostgreSQL — documentação da versão 18 — postgresql.org
  6. Imagem oficial do PostgreSQL no Docker — hub.docker.com

Continue por aqui