Erros comuns em Prisma ORM que você deve evitar
Se você usa Prisma no dia a dia, sabe que ele acelera muito. O problema é quando decisões pequenas (e repetidas)
viram custo, bugs difíceis e consultas desnecessárias. Abaixo eu separo os erros mais frequentes e como contornar.
1Ignorar modelagem e deixar o schema “genérico demais”
Um schema fraco custa caro: vira validação no lugar errado, consultas mais complexas e consistência
dependente da aplicação. Isso aparece quando você:
-
Permite null em campos que deveriam ser obrigatórios (ou o inverso: força obrigatório
onde a regra de negócio não garante). -
Não define relacionamentos com cardinalidade e campos explícitos (relation fields), o que
dificulta queries e dificulta entender o domínio. - Deixa índices e unicidades para “depois”, e o banco começa a sofrer com filtros e joins frequentes.
Se a regra existe, ela deve refletir no banco (e não somente em validações do frontend/rota).
2Ficar sem controle de N+1 e carregar dados demais
O erro aqui não é “usar Prisma”. É montar o fluxo de dados de um jeito que o banco recebe
muitas consultas ou retorna colunas e relações desnecessárias. Os sinais mais comuns:
-
Loop no código chamando
findUnique/findFirstpara cada item de uma lista.
Isso gera N consultas em vez de 1 (ou poucas). -
Uso excessivo de
includesemselecte sem critério.
Você carrega “tudo”, mesmo quando só precisa de poucos campos. -
Consultas com filtros aplicados em memória após buscar os dados.
O banco faz muito melhor quando o filtro vai no where.
reduza no banco; se precisa de relações, traga somente as relações e campos necessários.
3Atualizações parciais e inconsistentes (upsert sem pensar no conjunto)
É comum “resolver rápido” com upsert, mas isso exige atenção ao que compõe a integridade.
Os problemas aparecem quando:
-
O where do
upsertnão usa uma chave realmente única
(ou seja: a identificação está fraca). - Você atualiza alguns campos, mas ignora campos derivados, contadores ou colunas que dependem do estado.
-
Você não considera concorrência: duas requisições ao mesmo tempo podem criar inconsistências
caso a modelagem não garanta unicidade e atomicidade.
- Você faz update em cascata manual (ex.: atualiza tabela A e depois B) sem transação.
- Você tenta corrigir a regra após o fato, com “ajustes” que passam testes locais, mas falham em produção.
- Falta um unique constraint (no schema) para garantir o que o código assume.
que precisam ficar coerentes (ex.: registro principal + auditoria + atualização de estatísticas).
4Transactions e erros de tipo: confiar demais em runtime
Dois pontos que aparecem em manutenção: (1) transações sem estratégia e (2) confundir “tipo” e “valor”.
No Prisma, você ganha tipagem do client, mas ainda assim precisa garantir:
-
Tratamento correto de falha por inexistência (ex.: quando o registro não existe).
Não presuma queupdatevai criar; ela falha. - Decisões com base em condições que podem mudar entre leitura e escrita (race conditions).
-
Uso de transações (
prisma.$transaction) quando existem múltiplas operações dependentes.
Sem isso, você pode gravar parte da alteração e deixar o restante inconsistente. -
Cuidado com campos de data: comparações de
DateTimeexigem consistência de timezone
e validação do formato.
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
export async function finalizarPedido(userId: string, total: number) {
return prisma.$transaction(async (tx) => {
// 1) Garanta que o carrinho/estado existe antes (e considere “lock” via estratégia no banco quando necessário)
const carrinho = await tx.carrinho.findUnique({
where: { userId }, // precisa ser uma chave única real
select: { id: true, status: true }
});
if (!carrinho) {
throw new Error("Carrinho não encontrado.");
}
if (carrinho.status !== "ABERTO") {
throw new Error("Carrinho não está disponível para finalização.");
}
// 2) Atualize o registro principal
const pedido = await tx.pedido.create({
data: {
userId,
total,
status: "PAGO"
},
select: { id: true }
});
// 3) Registre evento/auditoria (escrita dependente)
await tx.evento.create({
data: {
pedidoId: pedido.id,
tipo: "PEDIDO_FINALIZADO",
detalhes: { total }
}
});
// 4) Atualize estado do carrinho
await tx.carrinho.update({
where: { id: carrinho.id },
data: { status: "FINALIZADO" }
});
return pedido;
});
}
Tudo fica coerente porque as operações dependem do mesmo “bloco” transacional.
Quer melhorar ainda mais seu Prisma?
Se você curtiu esse conteúdo e quer evoluir com práticas que evitam retrabalho, vale a pena ler os próximos posts:
Sou Apaixonado pela programação e estou trilhando o caminho de ter cada diz mais conhecimento e trazer toda minha experiência vinda do Design para a programação resultando em layouts incríveis e idéias inovadoras! Conecte-se Comigo!