Relações no Prisma: include, connect e nested writes
Modele relações um-para-muitos no Prisma 7 e execute include, nested select, connect, filtros relacionais e exclusão protegida por foreign key.
Relações no Prisma ligam models por campos de navegação e uma chave persistida no
banco. Numa relação um-para-muitos, Autor tem livros Livro[]; Livro tem
autor Autor e autorId Int. Depois disso, include, nested writes, connect
e filtros relacionais trabalham sobre a foreign key do PostgreSQL.
Esta lição parte dos models e do CRUD já funcionando. Se esses passos ainda não estão no seu projeto, revise schema e modelos e CRUD com Prisma Client.
Ficha do livro e cadastro da autora: o modelo mental
Imagine duas fichas físicas. A ficha da autora tem seu número de cadastro. A ficha de cada livro guarda esse número, em vez de copiar nome e email da autora. Quando alguém pede os livros com suas autoras, o sistema cruza o número das duas fichas.
No mapa técnico, Autor.id é a chave primária, Livro.autorId é a foreign key e
o cruzamento corresponde ao JOIN. Os relation fields autor e livros dão ao
Prisma uma forma de navegar e escrever esse vínculo. O limite da analogia: a
lista livros não é armazenada como uma pilha dentro da linha de autora; ela é
obtida consultando as linhas de Livro que apontam para o id.
Volte ao comportamento do banco sempre que a sintaxe parecer mágica. A lição de JOIN no PostgreSQL mostra o cruzamento em SQL que existe por baixo das leituras relacionais.
Os dois lados precisam aparecer no schema
Modele a relação um-para-muitos:
model Autor {
id Int @id @default(autoincrement())
nome String
email String @unique
livros Livro[]
@@map("autores")
}
model Livro {
id Int @id @default(autoincrement())
titulo String
slug String @unique
autorId Int @map("autor_id")
autor Autor @relation(fields: [autorId], references: [id], onDelete: Restrict)
@@index([autorId])
@@map("livros")
}autorId vira autor_id no PostgreSQL. fields diz qual escalar guarda a chave;
references aponta para Autor.id. onDelete: Restrict recusa apagar a autora
enquanto livros a referenciam. O índice ajuda as consultas que chegam aos livros
por autora.
livros Livro[] não aceita ?: uma lista sem relacionados é vazia, não nula. A
relação do lado Livro é obrigatória porque autorId e autor também não são
opcionais.
A migration prova a foreign key
Depois de prisma migrate dev, confira o SQL:
ALTER TABLE "livros"
ADD CONSTRAINT "livros_autor_id_fkey"
FOREIGN KEY ("autor_id")
REFERENCES "autores"("id")
ON DELETE RESTRICT
ON UPDATE CASCADE;Essa constraint é a garantia persistida. Mesmo que outro script escreva SQL sem
Prisma, o PostgreSQL impede um autor_id órfão. Prisma gera API e tipos; a
foreign key protege todas as portas de entrada do banco.
Nested create grava o conjunto ou nada
Crie autora e livro numa única operação:
const autora = await prisma.autor.create({
data: {
nome: "Machado de Assis",
email: "machado@example.com",
livros: {
create: {
titulo: "Memórias Póstumas de Brás Cubas",
slug: "memorias-postumas",
preco: "44.90",
estoque: 10,
status: "PUBLICADO",
},
},
},
include: { livros: true },
});O nome técnico é nested write. Em português direto, uma escrita inclui outra pela relation. Prisma executa o conjunto com garantia transacional: se o slug do livro violar uma constraint, a autora dessa operação não fica cadastrada sem o livro.
console.log("NESTED CREATE", {
autor: autora.nome,
livros: autora.livros.map((livro) => livro.titulo),
});Include traz a relação; select controla a forma
Sem include ou select relacional, Prisma retorna campos escalares do model e
não carrega relações automaticamente. Peça os livros, a contagem e uma ordem:
const autores = await prisma.autor.findMany({
orderBy: { nome: "asc" },
include: {
livros: {
orderBy: { titulo: "asc" },
select: { titulo: true },
},
_count: { select: { livros: true } },
},
});include acrescenta dados relacionados à forma padrão de Autor. Dentro dele,
o nested select limita Livro a título. _count pede contagem sem devolver os
objetos apenas para contar no JavaScript.
console.log(autores.map((autor) => ({
autor: autor.nome,
total: autor._count.livros,
livros: autor.livros.map((livro) => livro.titulo),
})));Não use select e include no mesmo nível. Se precisa de poucos campos do model
principal e da relation, faça um select principal e aninhe a relation nele.
Filtros relacionais respondem perguntas sobre o outro lado
Para relações em lista, some, every e none expressam quantificadores.
Busque autoras com pelo menos um livro acima de cinquenta e cinco reais:
const comLivroCaro = await prisma.autor.findMany({
where: {
livros: {
some: { preco: { gt: "55.00" } },
},
},
select: { nome: true },
});
console.log("FILTER", comLivroCaro);some significa “existe pelo menos um”. every exige que todos os relacionados
atendam ao filtro; numa lista vazia, essa condição é matematicamente verdadeira,
o que pode surpreender. none exige que nenhum atenda. Escolha o quantificador
pela pergunta do domínio.
Connect aponta para um registro já existente
Transfira o livro de Machado para Conceição localizando a autora por email único:
const reconectado = await prisma.livro.update({
where: { slug: "memorias-postumas" },
data: {
autor: {
connect: { email: "conceicao@example.com" },
},
},
select: {
titulo: true,
autor: { select: { nome: true } },
},
});
console.log("CONNECT", reconectado);connect não cria autora; ela precisa existir e o where precisa ser único.
Tecnicamente, o update altera autor_id. Num sistema real, “transferir autoria”
provavelmente não seria permitido: o exemplo revela o mecanismo, não recomenda
a regra de negócio.
O lado oposto ausente falha na validação
Retire livros Livro[] de Autor, mantendo Livro.autor. prisma validate
reproduz:
npx prisma validateValidation Error Count: 1 Prisma CLI Version : 7.9.1
O erro não pede outra coluna em Autor. Ele pede o relation field oposto para o
Prisma descrever a navegação. Recoloque livros Livro[], formate e valide.
Restrict impede apagar o pai referenciado
Com três livros apontando para Conceição, tente remover a autora:
import { Prisma } from "./generated/prisma/client.js";
try {
await prisma.autor.delete({
where: { email: "conceicao@example.com" },
});
} catch (erro) {
if (erro instanceof Prisma.PrismaClientKnownRequestError) {
console.error("DELETE PARENT", {
code: erro.code,
modelName: erro.meta?.modelName,
});
}
}No teste com Prisma 7.9.1 e adapter-pg, o detalhe do driver informa que a
operação viola RESTRICT da constraint livros_autor_id_fkey. Não trate apagando
livros em cascata automaticamente. Decida se o domínio pede impedir, transferir,
anonimizar, tornar opcional ou apagar dependentes. Depois represente a decisão
em onDelete e no fluxo da aplicação.
Missão: ligue pedido, item e livro
Modele Pedido e ItemPedido. Um pedido tem muitos itens; cada item aponta para
um livro e guarda quantidade e precoUnitario. Crie índices nas duas foreign
keys e uma unique composta que impeça o mesmo livro duas vezes no mesmo pedido.
@@unique([pedidoId, livroId])Seu critério de sucesso: um nested create grava pedido com dois itens; uma leitura
devolve itens e títulos com nested select; some encontra pedidos com determinado
livro; repetir o livro no mesmo pedido falha pela unique; apagar um livro usado é
recusado pela foreign key.
Depois confira a migration SQL e escreva o JOIN equivalente. Quando a relação
estiver visível nos dois níveis, siga para migrations no Prisma
e aprenda a alterar essa estrutura sem perder os registros existentes.
Perguntas frequentes
Relation field vira coluna no PostgreSQL?
Qual a diferença entre include e select?
Nested create usa transação?
Connect cria o registro relacionado?
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, Prisma ORM 7.9.1, @prisma/adapter-pg 7.9.1, PostgreSQL 18.6 em postgres:18-alpine e Docker 29.5.3, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- Prisma Client — relation queries — prisma.io
- Prisma Schema — relations — prisma.io
- Prisma Schema — referential actions — prisma.io
- Prisma Client — select fields — prisma.io


