Prisma ORM em Node.js: Modelagem, Migrations e Queries Tipadas na Prática Já leu

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: Isso cria um arquivo e um diretório com o arquivo . Configure a variável de ambiente com seu banco de dados: Definindo Modelos com Prisma Schema O Prisma Schema é onde tudo começa. Ele define sua estrutura de dados em uma sintaxe limpa e intuitiva.

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.

Referências


Artigos relacionados