Ir para o conteúdo
Backend

Prisma ORM vs Drizzle ORM: comparação técnica e roteiro de benchmark reproduzível

Marcos Soares
Atualizado em 
9 minutos de leitura
Ilustracao 3D de dois objetos de vidro contrastantes com luz verde representando benchmark Prisma vs Drizzle ORM
Ouça este artigo
0:00Prisma ORM vs Drizzle ORM: comparação técnica e roteiro de benchmark reproduzível--:--

Conteúdo técnico toda semana

Receba artigos sobre arquitetura, padrões de projeto e engenharia de software. Direto no seu e-mail, sem enrolação.

Sem spam. Cancele a qualquer momento com 1 clique.

Neste artigo

Nota de setembro de 2026: a versão anterior deste artigo publicava tabelas de latência, throughput e memória apresentadas como "benchmark real", com números que não podem ser reproduzidos por quem lê (nem por este blog hoje: a máquina, o dataset e os dados brutos daquela rodada não foram preservados). Esses números saíram. O que fica é a comparação técnica, que se sustenta na arquitetura das duas bibliotecas, e um roteiro completo para você medir no seu ambiente. Quando este blog rodar o roteiro num repositório público, com lockfile e dados brutos, os números voltam com a fonte ao lado.

O debate que não para

Prisma ou Drizzle? A pergunta domina fóruns e threads desde 2024, e a maioria das comparações repete opiniões. Este artigo faz outra coisa: explica de onde vem cada diferença de comportamento e entrega o código para você confirmar com o seu banco e o seu workload.

As duas arquiteturas, sem números

Como o Prisma executa uma query

Na configuração clássica, o Prisma Client conversa com um query engine em Rust, que traduz a consulta para SQL e devolve o resultado. Cada consulta atravessa serialização, o processo do engine e a deserialização. É um custo fixo por query, independente de ela ser trivial ou complexa.

Desde o Prisma 6, com driver adapters (@prisma/adapter-pg para PostgreSQL) e engineType = "client", o engine binário sai do caminho: o Prisma passa a gerar o SQL no próprio processo Node.js e a usar o driver pg diretamente. Este blog roda exatamente nessa configuração em produção, com @prisma/adapter-pg sobre um pg.Pool, e a documentação oficial descreve os limites atuais dos adapters.

TYPESCRIPT
import { PrismaClient } from "@prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
import { Pool } from "pg";
 
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const adapter = new PrismaPg(pool);
const prisma = new PrismaClient({ adapter });

Como o Drizzle executa uma query

O Drizzle é um construtor de SQL tipado sobre o driver nativo (postgres, pg, node-postgres). A consulta que você escreve em TypeScript vira uma string SQL quase literal, sem processo intermediário. Não há engine, e o pacote é pequeno, o que pesa em ambientes com cold start (serverless e edge).

Relações: duas queries ou um JOIN

Quando você usa include no Prisma, ele executa consultas separadas e junta o resultado no JavaScript (o comportamento padrão, chamado de "join strategy: query"; versões recentes permitem relationJoins para gerar JOIN no banco, e vale conferir a documentação da sua versão). No Drizzle, leftJoin gera um único JOIN no SQL, e a query builder de relações (db.query) também resolve em uma ida ao banco na maioria dos casos.

A consequência prática: com relações, a diferença entre os dois não é "um é mais rápido"; é "quantas idas ao banco cada um faz para a mesma pergunta". Isso você enxerga com EXPLAIN e com o log de queries, sem cronômetro.

Bundle e cold start

O Prisma com engine binário carrega um executável; com adapter, carrega apenas JavaScript, mas o cliente gerado continua maior que o do Drizzle. Em funções serverless, meça o tempo de inicialização com o adapter ligado antes de concluir qualquer coisa: o cenário mudou muito entre o Prisma 5 e o 6.

Roteiro de benchmark que você roda

O roteiro abaixo é o mesmo que a versão anterior deste artigo descrevia, agora apresentado como procedimento, e não como resultado. Registre versões (npx prisma --version, npm ls drizzle-orm postgres pg), hardware, versão do PostgreSQL e o seed antes de comparar qualquer coisa.

Ambiente

  • PostgreSQL 16 local ou em contêiner, na mesma máquina dos scripts (latência de rede não entra na conta).
  • Node.js 22 LTS.
  • Dataset: 100.000 registros em users e 500.000 em posts, gerados pelo seu seed e versionados junto com os scripts.
  • Ferramenta: hyperfine para os comandos e performance.now() dentro dos scripts.

Projetos

Bash
mkdir bench-prisma && cd bench-prisma
npm init -y
npm install prisma @prisma/client @prisma/adapter-pg pg
npx prisma init --datasource-provider postgresql
Bash
mkdir bench-drizzle && cd bench-drizzle
npm init -y
npm install drizzle-orm postgres
npm install -D drizzle-kit

Schema compartilhado

Prisma (schema.prisma):

Prisma
generator client {
  provider   = "prisma-client-js"
  engineType = "client"
}
 
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}
 
model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String
  posts     Post[]
  createdAt DateTime @default(now())
}
 
model Post {
  id        Int      @id @default(autoincrement())
  title     String
  content   String
  published Boolean  @default(false)
  author    User     @relation(fields: [authorId], references: [id])
  authorId  Int
  createdAt DateTime @default(now())
}
Bash
npx prisma migrate dev --name init
npx prisma generate

Drizzle (schema.ts):

TYPESCRIPT
import { pgTable, serial, text, boolean, integer, timestamp } from "drizzle-orm/pg-core";
 
export const users = pgTable("users", {
  id: serial("id").primaryKey(),
  email: text("email").unique().notNull(),
  name: text("name").notNull(),
  createdAt: timestamp("created_at").defaultNow(),
});
 
export const posts = pgTable("posts", {
  id: serial("id").primaryKey(),
  title: text("title").notNull(),
  content: text("content").notNull(),
  published: boolean("published").default(false),
  authorId: integer("author_id").references(() => users.id).notNull(),
  createdAt: timestamp("created_at").defaultNow(),
});
Bash
npx drizzle-kit generate
npx drizzle-kit migrate

Os cinco cenários

  1. Select simples: buscar um usuário por ID.
  2. Select com relação: buscar usuário com seus posts.
  3. Insert em lote: inserir 1.000 registros.
  4. Update condicional: atualizar posts de um autor.
  5. Agregação: contar posts publicados por usuário.

Cada cenário roda 1.000 iterações depois de 100 iterações de aquecimento descartadas, e a comparação usa a mediana de pelo menos 5 rodadas, não a média de uma.

Cenário 1: select simples

Prisma:

TYPESCRIPT
import { PrismaClient } from "@prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
import { Pool } from "pg";
 
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const prisma = new PrismaClient({ adapter: new PrismaPg(pool) });
 
async function benchSelectSimple() {
  const start = performance.now();
 
  for (let i = 0; i < 1000; i++) {
    const id = Math.floor(Math.random() * 100000) + 1;
    await prisma.user.findUnique({ where: { id } });
  }
 
  const elapsed = performance.now() - start;
  console.log(`Prisma select simples: ${elapsed.toFixed(2)}ms total`);
  console.log(`Mediana por query: calcule sobre as rodadas, não sobre a média`);
}
 
benchSelectSimple().finally(() => prisma.$disconnect());

Drizzle:

TYPESCRIPT
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
import { eq } from "drizzle-orm";
import { users } from "./schema";
 
const client = postgres(process.env.DATABASE_URL!);
const db = drizzle(client);
 
async function benchSelectSimple() {
  const start = performance.now();
 
  for (let i = 0; i < 1000; i++) {
    const id = Math.floor(Math.random() * 100000) + 1;
    await db.select().from(users).where(eq(users.id, id)).limit(1);
  }
 
  const elapsed = performance.now() - start;
  console.log(`Drizzle select simples: ${elapsed.toFixed(2)}ms total`);
}
 
benchSelectSimple().finally(() => client.end());

Cenário 2: select com relação

Prisma:

TYPESCRIPT
await prisma.user.findUnique({
  where: { id },
  include: { posts: { take: 10 } },
});

Drizzle:

TYPESCRIPT
import { users, posts } from "./schema";
import { eq } from "drizzle-orm";
 
const result = await db
  .select()
  .from(users)
  .leftJoin(posts, eq(users.id, posts.authorId))
  .where(eq(users.id, id))
  .limit(10);

Ligue o log de queries dos dois (log: ["query"] no Prisma, logger: true no Drizzle) e conte as idas ao banco antes de olhar o tempo.

Cenário 3: insert em lote

Prisma:

TYPESCRIPT
await prisma.post.createMany({
  data: batchOf1000Posts,
  skipDuplicates: true,
});

Drizzle:

TYPESCRIPT
await db.insert(posts).values(batchOf1000Posts).onConflictDoNothing();

Como registrar o resultado

Para cada cenário, guarde: versão das bibliotecas, EXPLAIN (ANALYZE, BUFFERS) da query gerada, quantidade de queries por operação, mediana e p95 das rodadas, e o RSS do processo antes e depois (process.memoryUsage().rss). Publique o repositório com o lockfile. Sem isso, é opinião.

Onde cada um se destaca

Developer experience

O Prisma Studio, a introspecção de banco e o prisma migrate são maduros. O Prisma Client gera tipos a partir do schema sem configuração extra. O Drizzle exige mais SQL do time, e em troca mostra exatamente o que vai para o banco.

Ecossistema

O Prisma tem documentação extensa e soluções para a maioria dos casos de borda. O Drizzle cresce rápido, mas ainda há lacunas de documentação e mudanças de API entre versões menores; leia o changelog antes de atualizar.

Produtos pagos

O Prisma oferece Accelerate (pooling global com cache) e Pulse (streams de mudança). São úteis se você quer o ecossistema, e são um fator de dependência se você não quer.

Quando usar cada um

Drizzle: APIs onde o custo por query importa, edge functions e serverless com cold start sensível, times confortáveis com SQL, controle total do SQL gerado.

Prisma: times vindos do frontend, prioridade em DX, migrações com histórico e ferramentas visuais, e projetos sem requisito extremo de latência. Com driver adapters, o argumento de performance contra o Prisma perdeu força; meça antes de decidir por ele.

Os dois juntos: Prisma para migrações e tipos, Drizzle (ou SQL puro) para as consultas críticas.

TYPESCRIPT
// Queries normais com Prisma
const user = await prisma.user.findUnique({ where: { id } });
 
// Query crítica com Drizzle
const analytics = await db
  .select({
    authorId: posts.authorId,
    totalPosts: sql<number>`count(*)`,
    avgLength: sql<number>`avg(length(${posts.content}))`,
  })
  .from(posts)
  .where(eq(posts.published, true))
  .groupBy(posts.authorId)
  .orderBy(sql`count(*) desc`)
  .limit(100);

Typed SQL no Prisma

O Prisma permite escrever SQL puro com tipos gerados a partir do arquivo .sql:

TYPESCRIPT
import { prisma } from "./client";
import { getUserAnalytics } from "@prisma/client/sql";
 
const result = await prisma.$queryRawTyped(getUserAnalytics(userId));

Isso reduz a vantagem do Drizzle nas consultas complexas, sem trocar de ORM.

Posição editorial

Este blog usa Prisma 6 com @prisma/adapter-pg em produção. A escolha veio da DX das migrações e do cliente tipado, e o adapter resolveu o que mais incomodava na configuração com engine. O Drizzle é a escolha natural para quem prioriza controle do SQL e tamanho de bundle; nada aqui diz que ele é "mais lento" ou "mais rápido" sem o seu número na tabela.

Na maioria dos projetos, o gargalo não é o ORM: é query N+1, índice ausente ou cache inexistente. Meça o que dói antes de trocar a biblioteca.

Leia também

Marcos Soares

Escrito por

Marcos Soares

Fullstack Developer · CEO da Agência Poti

Fullstack Developer e CEO da Agência Poti. Mais de 20 anos construindo arquiteturas cloud-native com React, Next.js e sistemas distribuídos. Parceiro comercial do estúdio iellou design. Fundador do Vivo de Código.

Comentários

Participe da discussão

Seja o primeiro a comentar!

Continue Aprofundando

Guias de integração relacionados

Conteúdo técnico toda semana

Receba artigos sobre arquitetura, padrões de projeto e engenharia de software. Direto no seu e-mail, sem enrolação.

Sem spam. Cancele a qualquer momento com 1 clique.