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.
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:
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-alpineO 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:
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;'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:
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 --versionSistema 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:
npx prisma init \
--datasource-provider postgresql \
--output ../src/generated/prismaprisma/ 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:
printf 'DATABASE_URL="postgresql://nest:%s@127.0.0.1:55440/mentorias?schema=public"\n' \
"$PG_APP_PASSWORD" > .env
unset PG_APP_PASSWORDO 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:
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:
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:
npx prisma validate
npx prisma migrate dev --name criar_mentorias
npx prisma generateApplying 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:
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:
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:
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:
@Module({
imports: [AuthModule, PrismaModule],
controllers: [MentoriasController],
providers: [MentoriasService],
})
export class MentoriasModule {}E troque o array do service pelo Client injetado:
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:
set -a
source .env
set +a
JWT_SECRET="$(openssl rand -hex 32)" npm run startFaça login sem imprimir o token e crie duas mentorias:
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"}'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:
docker exec nestjs-prisma-pg psql -U nest -d mentorias \
-c 'SELECT id, titulo, vagas FROM mentorias ORDER BY id;'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:
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:
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:
DATABASE_URL='postgresql://nest:senha-local@127.0.0.1:59999/mentorias' \
npx prisma migrate statusPlease 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.
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:
docker rm -f nestjs-prisma-pg
docker volume rm nestjs-prisma-pgO 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.
Perguntas frequentes
Prisma 7 precisa de adapter no NestJS?
Por que criar PrismaService?
Onde fica DATABASE_URL no Prisma 7?
Posso usar migrate dev em produção?
Qual é o mount correto da imagem PostgreSQL 18?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
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
- NestJS — receita oficial com Prisma — docs.nestjs.com
- Prisma ORM — driver adapters — prisma.io
- Prisma ORM — geração do Client — prisma.io
- Prisma Migrate — desenvolvimento e produção — prisma.io
- PostgreSQL — documentação da versão 18 — postgresql.org
- Imagem oficial do PostgreSQL no Docker — hub.docker.com


