Introdução ao Prisma ORM
O Prisma é um ORM (Object-Relational Mapping) moderno para Node.js que resolve um dos maiores desafios da programação backend: gerenciar dados de forma segura e com type safety completo. Diferentemente de ORMs tradicionais como Sequelize ou TypeORM, o Prisma oferece uma abordagem schema-first, onde você define seu modelo de dados uma única vez e tudo mais é gerado automaticamente. Isso elimina redundância e reduz drasticamente bugs relacionados a tipos de dados.
Neste artigo, você aprenderá a configurar Prisma, modelar seus dados, executar migrations e escrever queries type-safe. Vamos direto ao essencial, sem teorias desnecessárias.
Configuração Inicial e Modelagem de Dados
Instalação e Setup
Comece criando um projeto Node.js e instalando Prisma:
npm init -y
npm install @prisma/client
npm install -D prisma
npx prisma init
Isso cria um arquivo .env e um diretório prisma/ com o arquivo schema.prisma. Configure a variável de ambiente com seu banco de dados:
DATABASE_URL="postgresql://user:password@localhost:5432/meu_banco"
Definindo Modelos com Prisma Schema
O Prisma Schema é onde tudo começa. Ele define sua estrutura de dados em uma sintaxe limpa e intuitiva. Veja um exemplo real:
// prisma/schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String
createdAt DateTime @default(now())
posts Post[] // Relação um-para-muitos
}
model Post {
id Int @id @default(autoincrement())
title String
content String
published Boolean @default(false)
authorId Int
author User @relation(fields: [authorId], references: [id])
comments Comment[]
}
model Comment {
id Int @id @default(autoincrement())
text String
postId Int
post Post @relation(fields: [postId], references: [id])
}
Cada modelo mapeia diretamente para uma tabela no banco de dados. Os atributos @id, @unique e @default definem constraints e comportamentos padrão. As relações (como posts Post[]) permitem navegação bidirecional tipada entre entidades.
Migrations: Sincronizando Banco e Schema
Criando e Executando Migrations
Migrations são a forma como Prisma controla versões de seu banco de dados. Após modificar o schema, execute:
npx prisma migrate dev --name nome_da_migracao
Isso cria um arquivo SQL em prisma/migrations/ e aplica no banco automaticamente. Por exemplo, adicionar um novo campo:
model User {
id Int @id @default(autoincrement())
email String @unique
name String
phone String? // Novo campo, opcional
createdAt DateTime @default(now())
posts Post[]
}
Execute npx prisma migrate dev --name add_phone_to_user e o Prisma gera:
ALTER TABLE "User" ADD COLUMN "phone" TEXT;
Para sincronizar sem criar nova migração (desenvolvimento), use:
npx prisma db push
Reset e Replicação
Em desenvolvimento, você pode resetar o banco:
npx prisma migrate reset
Isso desfaz todas as migrations, recria o schema do zero e executa seeds. Para produção, sempre revise as migrations antes de executar.
Queries Tipadas e Operações com Prisma Client
Operações CRUD Básicas
O Prisma Client fornece métodos type-safe para queries. TypeScript infere os tipos automaticamente:
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
// CREATE
const newUser = await prisma.user.create({
data: {
email: 'joao@example.com',
name: 'João Silva'
}
});
// READ
const user = await prisma.user.findUnique({
where: { email: 'joao@example.com' },
include: { posts: true } // Carrega relacionamentos
});
// UPDATE
const updated = await prisma.user.update({
where: { id: 1 },
data: { name: 'João Updated' }
});
// DELETE
await prisma.user.delete({
where: { id: 1 }
});
// LISTAR COM FILTROS
const recentPosts = await prisma.post.findMany({
where: {
published: true,
createdAt: {
gte: new Date('2024-01-01')
}
},
orderBy: { createdAt: 'desc' },
take: 10
});
Queries Avançadas e Relações
Prisma permite queries complexas com type safety total. Veja operações mais sofisticadas:
// Agregações
const totalPosts = await prisma.post.count({
where: { published: true }
});
// Nested writes (criar pai e filho simultaneamente)
const userWithPost = await prisma.user.create({
data: {
email: 'maria@example.com',
name: 'Maria',
posts: {
create: [
{ title: 'Primeiro Post', content: 'Conteúdo aqui' },
{ title: 'Segundo Post', content: 'Mais conteúdo' }
]
}
},
include: { posts: true }
});
// Transações para múltiplas operações atomicamente
await prisma.$transaction([
prisma.post.deleteMany({ where: { authorId: 5 } }),
prisma.user.delete({ where: { id: 5 } })
]);
// Raw queries quando necessário
const users = await prisma.$queryRaw`
SELECT * FROM "User" WHERE email LIKE ${`%example%`}
`;
O IntelliSense do TypeScript autocompleta campos e relações, evitando erros em tempo de desenvolvimento. Se você escrever prisma.user.findMany({ where: { invalidField: true } }), TypeScript avisa imediatamente.
Boas Práticas e Otimização
Gerenciamento de Conexões
Crie uma instância única do Prisma Client e reutilize-a:
// lib/prisma.ts
export const prisma = new PrismaClient();
// Importar em qualquer arquivo
import { prisma } from './lib/prisma';
Para aplicações serverless, considere usar pool de conexões:
DATABASE_URL="postgresql://user:pass@localhost/db?schema=public"
Seleção Eficiente de Campos
Use select para trazer apenas os dados necessários, reduzindo payload:
const users = await prisma.user.findMany({
select: {
id: true,
email: true,
name: true,
posts: {
select: { title: true }
}
}
});
Tratamento de Erros
Prisma lança exceções tipadas. Trate-as apropriadamente:
import { Prisma } from '@prisma/client';
try {
await prisma.user.create({
data: { email: 'duplicado@example.com', name: 'Teste' }
});
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
if (error.code === 'P2002') {
console.log('Email já existe');
}
}
}
Conclusão
Você aprendeu que Prisma oferece type safety automático, eliminando erros de tipos em queries — algo que ORMs antigos não fazem nativamente. Segundo, migrations são simples e versionadas, facilitando colaboração em equipe e deploy seguro. Terceiro, a sintaxe é intuitiva e produtiva, reduzindo boilerplate significativamente em relação a SQL puro ou ORMs concorrentes.
Prisma transforma a forma como você trabalha com dados, tornando o código mais confiável e mantível. Pratique criando um projeto pequeno e explore a documentação oficial para casos de uso avançados.