Guia prático do Drizzle ORM: a alternativa TypeScript 90% mais leve que o Prisma

Na semana passada, fiz o deploy de um projeto no Vercel e esperei três segundos inteiros até a página inicial abrir. Três segundos! A essa altura, o usuário já foi embora.
Abri a análise do bundle no Vercel e fiquei olhando para o Prisma Client ocupando 14 MB, tentando entender como chegamos ali. Era apenas uma consulta simples de usuário; por que carregar tanto código? Para piorar, o cold start do Lambda era desesperador: uma função sem Prisma iniciava em 600 ms, enquanto a versão com Prisma levava 2,5 segundos.
Você pode pensar: mas o Prisma não é fácil de usar? É, e eu também pensava assim. Ele oferece segurança de tipos, migrações automáticas e a interface visual do Prisma Studio. O problema é que ele é pesado demais.
Se você também enfrenta cold starts lentos em serverless, bundles acima do limite ou consultas complexas que acabam em raw SQL, este artigo é para você. O Drizzle ORM tem um pacote principal de apenas 7,4 kB, mais de 90% menor que o Prisma, sem abrir mão da segurança de tipos.
Neste guia, vamos configurar Next.js com Drizzle do zero, entender sua API semelhante a SQL e comparar em detalhes seu desempenho com o Prisma. E, principalmente, veremos quando escolher Drizzle e quando o Prisma ainda é a melhor opção.
Por que precisamos do Drizzle? Os problemas dos ORMs atuais
Três pontos fracos do Prisma
O Prisma é uma boa escolha em muitos cenários. Mas, com o tempo, alguns problemas ficam difíceis de contornar.
Problema 1: o bundle cresce sem controle
O Client gerado pelo Prisma v5 pode chegar a 14 MB. Para ter uma ideia, um projeto Next.js inteiro pode ocupar apenas 2 ou 3 MB; o Prisma sozinho passa a dominar o pacote. O Prisma 7 reduziu o tamanho para 1 MB ao remover o binário Rust, mas versões antigas e projetos sensíveis ao tamanho do bundle ainda sofrem bastante.
Nossa equipe tinha um aplicativo de chat em tempo real que precisava ser implantado no Cloudflare Workers, onde o bundle não podia passar de 1 MB. O Prisma excedeu o limite e tivemos de trocar de solução.
Problema 2: cold start lento em serverless
A issue #10724 no GitHub discute há anos o cold start lento do Prisma no Lambda. Os números são diretos:
- Função sem ORM: cerca de 600 ms
- Função com Prisma v5: cerca de 2,5 s
- Função com Prisma v7: cerca de 1,5 s, uma melhora que ainda deixa a inicialização lenta
Isso acontece porque o Prisma precisa analisar, durante a inicialização, uma enorme string DMMF, ou Data Model Meta Format. Em um schema de porte médio, ela pode ter mais de seis milhões de caracteres. Fazer essa análise a cada cold start inevitavelmente custa tempo.
O Cal.com, uma ferramenta open source de agendamento, publicou um artigo técnico específico sobre as tentativas de otimizar esse tempo. A conclusão foi que o problema existe e só pode ser atenuado com vários workarounds.
Problema 3: pouco controle sobre SQL
A filosofia do Prisma é abstrair o SQL para que as consultas sejam escritas na DSL do Prisma Client. Isso é conveniente na maior parte do tempo, mas complica consultas avançadas.
Imagine um JOIN em várias camadas com subconsulta e agregação condicional. A API do Prisma pode não conseguir expressá-lo, obrigando você a recorrer a prisma.$queryRaw e escrever SQL diretamente.
Se o resultado final ainda é SQL, por que não adotar desde o início uma API semelhante a SQL? Essa é a proposta do Drizzle.
O que os desenvolvedores realmente precisam
Em resumo, estas são as necessidades mais comuns:
- Segurança de tipos sem sacrificar desempenho: queremos a inferência do TypeScript, mas não um ORM que deixe o projeto lento.
- Usar SQL sem aprender outra DSL: SQL já resolve o problema; não deveria ser necessário memorizar outra sintaxe de consulta.
- Boa integração com serverless: em 2025, Vercel, Cloudflare Workers e AWS Lambda já fazem parte do cotidiano.
- Compilação rápida: em projetos grandes, a inferência do Prisma também pode desacelerar a compilação TypeScript.
O Drizzle foi criado para atender justamente a essas necessidades.
O que é o Drizzle ORM? Principais recursos
A filosofia do Drizzle
O slogan do Drizzle é “If you know SQL, you know Drizzle”. Parece ousado, mas descreve bem a ferramenta.
"If you know SQL, you know Drizzle."
Enquanto ORMs tradicionais tentam esconder o SQL, o Drizzle faz o contrário: ele não abstrai SQL, mas aproxima sua API TypeScript o máximo possível da sintaxe SQL. O código se parece com SQL e ainda oferece dicas completas de tipos.
Veja este exemplo:
// Consulta com Drizzle
await db
.select()
.from(posts)
.leftJoin(comments, eq(posts.id, comments.postId))
.where(eq(posts.id, 10))
// SQL gerado
SELECT * FROM posts
LEFT JOIN comments ON posts.id = comments.post_id
WHERE posts.id = 10
A estrutura do código é praticamente igual à do SQL. Quem já conhece a linguagem entende a consulta imediatamente.
Principais recursos
1. Extremamente leve
O pacote principal drizzle-orm tem apenas 7,4 kB com minificação e gzip, além de nenhuma dependência em runtime. Compare:
- Drizzle: cerca de 7,4 kB
- TypeORM: cerca de 300 kB
- Prisma v7: cerca de 1 MB
- Prisma v5: cerca de 14 MB
Não são diferenças pequenas; estamos falando de outra ordem de grandeza.
2. TypeScript-first, sem gerar Client
O Prisma exige a execução de prisma generate para criar o Client. O Drizzle não.
Depois que você define o schema, o TypeScript infere os tipos diretamente. IntelliSense, preenchimento automático, verificação de tipos e mensagens de erro funcionam sem uma etapa de geração. Os problemas aparecem na compilação, antes de chegar ao runtime.
3. API semelhante a SQL e curva de aprendizado mínima
Se você já escreve SQL, consegue começar com o Drizzle em menos de dez minutos.
// SELECT
db.select().from(users).where(eq(users.id, 1))
// INSERT
db.insert(users).values({ name: 'John', email: '[email protected]' })
// UPDATE
db.update(users).set({ name: 'Jane' }).where(eq(users.id, 1))
// DELETE
db.delete(users).where(eq(users.id, 1))
Quem conhece SQL entende essas operações sem precisar abrir a documentação.
4. Sem perda de desempenho
O Drizzle não adiciona uma camada de abstração em runtime. A consulta escrita por você é traduzida diretamente para SQL, sem etapas intermediárias.
Enquanto o Prisma analisa o DMMF na inicialização e mantém estado interno, o Drizzle funciona como um query builder direto: sem custo oculto ou sobrecarga inesperada.
5. Pronto para serverless
O Drizzle funciona nos principais ambientes serverless:
- Vercel Edge Functions
- Cloudflare Workers
- AWS Lambda
- Deno Deploy
- Bun
Também oferece suporte nativo a drivers de banco serverless:
- Neon Serverless
- PlanetScale
- Turso, com SQLite on the edge
- Supabase
Em nosso projeto com Neon e Drizzle no Vercel Edge, o cold start caiu de 2,5 s para 700 ms. Esse foi o resultado real.
Quando usar
O Drizzle não serve para tudo, mas se destaca nestes cenários:
1. Aplicações serverless
Se a aplicação roda em Lambda ou Edge Functions, o pacote leve e o cold start rápido do Drizzle são vantagens fundamentais.
2. Cenários sensíveis a desempenho
Aplicações em tempo real, sistemas financeiros e plataformas de análise de dados se beneficiam diretamente do design sem abstrações adicionais.
3. Projetos que exigem controle sobre SQL complexo
Quando há muitas consultas avançadas ou necessidade de otimização manual, a API semelhante a SQL é mais flexível que a DSL do Prisma.
4. Projetos frontend sensíveis ao tamanho do bundle
Alguns frameworks full stack, como SolidStart e Qwik, podem empacotar o código do ORM no cliente. Nesses casos, os 7,4 kB do Drizzle fazem uma diferença enorme.
Por outro lado, se a equipe conhece pouco SQL, precisa criar um protótipo rapidamente ou prefere o conjunto de ferramentas do Prisma — Prisma Studio, Prisma Migrate e Prisma Pulse —, o Prisma ainda pode ser a melhor escolha.
Configuração prática de Next.js com Drizzle
Depois da teoria, vamos configurar do zero um projeto com Next.js 15, Drizzle e PostgreSQL.
Preparação do ambiente
Crie primeiro o projeto Next.js:
npx create-next-app@latest my-drizzle-app
cd my-drizzle-app
Instale as dependências do Drizzle:
npm install drizzle-orm drizzle-kit
npm install @neondatabase/serverless # Para usar o banco Neon
# ou
npm install postgres # Para um PostgreSQL tradicional
Recomendo o Neon, um PostgreSQL serverless que combina muito bem com o Drizzle. Crie uma conta gratuita, abra um banco e copie a string de conexão.
Defina o schema do banco de dados
Crie db/schema.ts com a estrutura das tabelas:
import { pgTable, serial, text, timestamp, integer } from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';
// Tabela de usuários
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
createdAt: timestamp('created_at').defaultNow(),
});
// Tabela de posts
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: text('title').notNull(),
content: text('content'),
authorId: integer('author_id').references(() => users.id),
createdAt: timestamp('created_at').defaultNow(),
});
// Relação de um para muitos
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}));
É apenas TypeScript. Não existe um arquivo de schema especial como no Prisma e não é necessário gerar um Client.
Configure a conexão com o banco
Crie db/index.ts:
import { drizzle } from 'drizzle-orm/neon-http';
import { neon } from '@neondatabase/serverless';
import * as schema from './schema';
// Lê a conexão do banco a partir da variável de ambiente
const sql = neon(process.env.DATABASE_URL!);
// Cria a instância do Drizzle
export const db = drizzle(sql, { schema });
Depois, adicione a variável a .env.local:
DATABASE_URL=postgres://user:[email protected]/dbname
Configure o Drizzle Kit para migrações
Crie drizzle.config.ts:
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './db/schema.ts',
out: './drizzle',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
});
Gere os arquivos de migração:
npx drizzle-kit generate
O comando cria as migrações SQL no diretório drizzle/. Revise o SQL gerado e, se estiver correto, execute:
npx drizzle-kit push
As tabelas do banco estarão prontas.
Estrutura final do projeto
my-drizzle-app/
├── app/ # Aplicação Next.js
│ ├── page.tsx
│ └── actions.ts # Server Actions
├── db/
│ ├── schema.ts # Tabelas e relações
│ └── index.ts # Conexão com o banco
├── drizzle/
│ └── migrations/ # Migrações geradas automaticamente
├── drizzle.config.ts # Configuração do Drizzle Kit
├── .env.local # Variáveis de ambiente
└── package.json
O processo inteiro leva menos de cinco minutos. Não há uma sintaxe especial como Prisma Schema nem uma espera por prisma generate: é TypeScript direto.
API semelhante a SQL do Drizzle na prática
Com tudo configurado, vamos ver as operações mais comuns e o uso em Server Actions do Next.js.
Operações CRUD básicas
Consulta de dados (SELECT)
import { db } from '@/db';
import { users, posts } from '@/db/schema';
import { eq, like, and, or, desc } from 'drizzle-orm';
// Consulta todos os usuários
const allUsers = await db.select().from(users);
// Consulta um usuário
const user = await db.select().from(users).where(eq(users.id, 1));
// Busca aproximada
const result = await db.select().from(users).where(like(users.name, '%John%'));
// Condições compostas
const admins = await db
.select()
.from(users)
.where(
and(
eq(users.role, 'admin'),
gt(users.createdAt, new Date('2024-01-01'))
)
);
// Ordenação e limite
const latestPosts = await db
.select()
.from(posts)
.orderBy(desc(posts.createdAt))
.limit(10);
A lógica é a mesma do SQL, expressa por chamadas de função.
Inserção de dados (INSERT)
// Insere um registro
await db.insert(users).values({
name: 'John Doe',
email: '[email protected]',
});
// Insere vários registros
await db.insert(users).values([
{ name: 'Alice', email: '[email protected]' },
{ name: 'Bob', email: '[email protected]' },
]);
// Retorna o registro inserido
const [newUser] = await db
.insert(users)
.values({ name: 'Charlie', email: '[email protected]' })
.returning();
console.log(newUser.id); // ID gerado automaticamente
Atualização de dados (UPDATE)
// Atualiza um registro
await db
.update(users)
.set({ name: 'Jane Doe' })
.where(eq(users.id, 1));
// Atualiza vários registros
await db
.update(posts)
.set({ published: true })
.where(eq(posts.authorId, 1));
// Retorna os dados atualizados
const [updatedUser] = await db
.update(users)
.set({ name: 'Updated Name' })
.where(eq(users.id, 1))
.returning();
Exclusão de dados (DELETE)
// Exclui um registro
await db.delete(users).where(eq(users.id, 1));
// Exclui vários registros
await db.delete(posts).where(eq(posts.published, false));
// Retorna os dados excluídos
const deleted = await db
.delete(users)
.where(eq(users.id, 1))
.returning();
Consultas avançadas
Operações JOIN
// LEFT JOIN: consulta usuários e seus posts
const usersWithPosts = await db
.select({
userId: users.id,
userName: users.name,
postId: posts.id,
postTitle: posts.title,
})
.from(users)
.leftJoin(posts, eq(users.id, posts.authorId));
// INNER JOIN: consulta apenas usuários que têm posts
const activeAuthors = await db
.select()
.from(users)
.innerJoin(posts, eq(users.id, posts.authorId));
Subconsultas
// Consulta usuários com mais de cinco posts
const sq = db
.select({ authorId: posts.authorId, count: count() })
.from(posts)
.groupBy(posts.authorId)
.having(gt(count(), 5))
.as('sq');
const prolificAuthors = await db
.select()
.from(users)
.innerJoin(sq, eq(users.id, sq.authorId));
Funções de agregação
import { count, sum, avg } from 'drizzle-orm';
// Conta o total de usuários
const [{ total }] = await db
.select({ total: count() })
.from(users);
// Conta posts por autor
const postCounts = await db
.select({
authorId: posts.authorId,
count: count(),
})
.from(posts)
.groupBy(posts.authorId);
Uso em Server Actions do Next.js
Crie app/actions.ts:
'use server';
import { db } from '@/db';
import { users } from '@/db/schema';
import { eq } from 'drizzle-orm';
import { revalidatePath } from 'next/cache';
// Cria um usuário
export async function createUser(formData: FormData) {
const name = formData.get('name') as string;
const email = formData.get('email') as string;
try {
await db.insert(users).values({ name, email });
revalidatePath('/users');
return { success: true };
} catch (error) {
return { success: false, error: 'Não foi possível criar o usuário' };
}
}
// Obtém todos os usuários
export async function getUsers() {
return await db.select().from(users);
}
// Exclui um usuário
export async function deleteUser(id: number) {
try {
await db.delete(users).where(eq(users.id, id));
revalidatePath('/users');
return { success: true };
} catch (error) {
return { success: false, error: 'Não foi possível excluir o usuário' };
}
}
Use a action na página:
// app/users/page.tsx
import { getUsers } from '../actions';
export default async function UsersPage() {
const users = await getUsers();
return (
<div>
<h1>Lista de usuários</h1>
<ul>
{users.map(user => (
<li key={user.id}>
{user.name} - {user.email}
</li>
))}
</ul>
</div>
);
}
Segurança de tipos no TypeScript
É aqui que o Drizzle realmente se destaca: a inferência completa de tipos.
// O tipo de retorno é inferido automaticamente
const users = await db.select().from(users);
// Tipo: { id: number; name: string; email: string; createdAt: Date }[]
// Ao escolher campos, o tipo também é inferido
const result = await db
.select({
id: users.id,
name: users.name,
})
.from(users);
// Tipo: { id: number; name: string }[]
// O erro é detectado na compilação
await db.select().from(users).where(eq(users.id, '1'));
// ❌ Erro do TypeScript: string não pode ser atribuído a number
O IntelliSense sugere campos, funções e operadores de condição disponíveis. Em muitos casos, basta digitar um ponto para ver tudo o que pode ser usado.
Você não precisa decorar a API nem consultar a documentação o tempo todo: o compilador TypeScript atua como uma documentação interativa.
Drizzle vs. Prisma: comparação detalhada
Até aqui falamos muito das vantagens do Drizzle. Para escolher com objetividade, precisamos comparar os dois lados.
Comparação de desempenho
| Critério | Drizzle | Prisma v5 | Prisma v7 | Observação |
|---|---|---|---|---|
| Tamanho do bundle | ~7,4 kB | ~14 MB | ~1 MB | Drizzle é o mais leve |
| Cold start | ~600 ms | ~2,5 s | ~1,5 s | Em ambiente serverless |
| Dependências em runtime | 0 | Binário Rust | 0 | Drizzle e Prisma v7 não têm dependências |
| Uso de memória | ~5 MB | ~80 MB | ~30 MB, estimado | Memória real em runtime |
| Velocidade da verificação de tipos | Rápida | Média | Média | Inferência mais simples no Drizzle |
Dados de um caso real
Depois que nossa equipe migrou um projeto do Prisma v5 para o Drizzle:
- A primeira requisição caiu de 3 s para 700 ms, uma melhora de 76%.
- O bundle de produção caiu de 18 MB para 4 MB, uma redução de 78%.
- O cold start do Lambda caiu de 2,4 s para 650 ms, ficando 73% mais rápido.
Esses números vieram do monitoramento real em produção, não de um benchmark artificial.
Comparação da experiência de desenvolvimento
Como o schema é definido
// Drizzle, em TypeScript
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
});
// Prisma, com DSL própria
model User {
id Int @id @default(autoincrement())
name String
}
Vantagem do Drizzle: é TypeScript, tem bom suporte da IDE e permite usar recursos como tipos condicionais e generics.
Vantagem do Prisma: o Prisma Schema é mais conciso e fácil de ler.
Estilo da API de consulta
// Drizzle, semelhante a SQL
await db
.select()
.from(users)
.leftJoin(posts, eq(users.id, posts.authorId))
.where(gt(posts.views, 1000));
// Prisma, com API encadeada
await prisma.user.findMany({
include: {
posts: {
where: { views: { gt: 1000 } },
},
},
});
Vantagem do Drizzle: por ficar mais próximo de SQL, expressa consultas complexas com mais facilidade.
Vantagem do Prisma: é mais amigável para quem conhece pouco SQL, e a semântica das consultas aninhadas é mais clara.
Comparação de recursos
Vantagens do Drizzle
- Extremamente leve: o núcleo de 7,4 kB é ideal para projetos sensíveis ao tamanho do bundle.
- Nativo para serverless: cold start rápido e compatibilidade com ambientes Edge.
- Controle sobre SQL: maior flexibilidade em consultas complexas e otimização.
- Sem gerar Client: altere o schema e use imediatamente, sem
prisma generate. - Tree-shakable: apenas o código utilizado entra no bundle.
Vantagens do Prisma
- Ecossistema maduro: lançado em 2021, tem uma comunidade ampla e muitos tutoriais.
- Ferramentas completas: Prisma Studio para administração visual, Prisma Migrate para migrações e Prisma Pulse para atualizações em tempo real.
- Consultas relacionais inteligentes: trata o problema de N+1 automaticamente e torna consultas aninhadas mais diretas.
- Amigável para iniciantes: não exige conhecimento profundo de SQL, e a DSL tem uma curva de aprendizado suave.
- Mensagens de erro detalhadas: os erros em runtime são mais fáceis de entender.
Recomendações para escolher
Escolha Drizzle quando
✅ O projeto roda em Vercel, Cloudflare Workers, Deno Deploy ou outro ambiente serverless ou Edge.
✅ O desempenho é crítico, como em aplicações em tempo real, sistemas financeiros e APIs de alta concorrência.
✅ O bundle precisa ficar abaixo de 1 MB.
✅ A equipe domina SQL e prefere controlar as consultas.
✅ O projeto exige otimização SQL complexa.
Escolha Prisma quando
✅ A equipe conhece pouco SQL e precisa começar rapidamente.
✅ A experiência de desenvolvimento e a cadeia de ferramentas são prioridades.
✅ É necessário administrar o banco visualmente com Prisma Studio.
✅ O projeto tem consultas relacionais e modelagem de dados complexas.
✅ A aplicação não roda em serverless e o tamanho do bundle não é crítico.
Minha recomendação pessoal
Em um projeto novo, eu escolheria assim:
- Projeto pessoal ou startup: Drizzle, pelo desempenho, custo baixo e potencial de otimização.
- Projeto empresarial ou em equipe: depende da experiência do time. Se todos dominam SQL, Drizzle; caso contrário, Prisma.
- Serverless-first: Drizzle sem hesitação.
- Servidor tradicional: ambos funcionam; o Prisma oferece uma cadeia de ferramentas mais completa.
Também existe uma abordagem híbrida: usar Drizzle nos módulos centrais sensíveis a desempenho e Prisma no painel administrativo. Os dois podem coexistir sem conflito.
Guia de migração e boas práticas
Migração do Prisma para o Drizzle
Se você já tem um projeto com Prisma e quer experimentar o Drizzle, pode migrar gradualmente.
Etapa 1: converter o schema
// Prisma Schema
model User {
id Int @id @default(autoincrement())
name String
email String @unique
posts Post[]
createdAt DateTime @default(now())
}
// Conversão para Drizzle Schema
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
createdAt: timestamp('created_at').defaultNow(),
});
Etapa 2: reescrever as consultas
// Consulta no Prisma
const user = await prisma.user.findUnique({
where: { id: 1 },
include: { posts: true },
});
// Consulta no Drizzle
const [user] = await db
.select()
.from(users)
.where(eq(users.id, 1))
.leftJoin(posts, eq(users.id, posts.authorId));
Etapa 3: substituir aos poucos
Prisma e Drizzle podem coexistir:
// Use Drizzle em consultas sensíveis a desempenho
import { db } from '@/db/drizzle';
const latestPosts = await db
.select()
.from(posts)
.orderBy(desc(posts.createdAt))
.limit(50);
// Mantenha temporariamente o Prisma em consultas relacionais complexas
import { prisma } from '@/db/prisma';
const userWithRelations = await prisma.user.findUnique({
where: { id: 1 },
include: {
posts: { include: { comments: { include: { author: true } } } },
},
});
Substitua uma parte de cada vez para reduzir o risco.
Boas práticas com Drizzle
1. Gerenciamento do pool de conexões
Em serverless, cuide especialmente do pool:
import { drizzle } from 'drizzle-orm/neon-http';
import { neon, neonConfig } from '@neondatabase/serverless';
// Configura o cache de conexões
neonConfig.fetchConnectionCache = true;
const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql, { schema });
2. Consultas preparadas
Consultas executadas com frequência podem ser preparadas para melhorar o desempenho:
import { db } from '@/db';
import { users } from '@/db/schema';
import { eq } from 'drizzle-orm';
// Prepara a consulta
const getUserById = db
.select()
.from(users)
.where(eq(users.id, placeholder('id')))
.prepare('get_user_by_id');
// Executa a consulta preparada
const user = await getUserById.execute({ id: 1 });
3. Transações
await db.transaction(async (tx) => {
// Cria o usuário
const [user] = await tx
.insert(users)
.values({ name: 'John', email: '[email protected]' })
.returning();
// Cria o post relacionado
await tx.insert(posts).values({
title: 'First Post',
authorId: user.id,
});
// Se qualquer etapa falhar, toda a transação será revertida
});
4. Reutilização de tipos
Exporte os tipos inferidos para o frontend:
// db/schema.ts
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull(),
});
// Exporta os tipos inferidos
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;
// Uso no frontend
import type { User } from '@/db/schema';
function UserCard({ user }: { user: User }) {
return <div>{user.name}</div>;
}
5. Tratamento de erros
import { db } from '@/db';
import { users } from '@/db/schema';
try {
await db.insert(users).values({
name: 'John',
email: '[email protected]',
});
} catch (error) {
// Código de erro do PostgreSQL
if (error.code === '23505') {
console.error('O e-mail já existe');
} else {
console.error('Erro no banco de dados', error);
}
}
Conclusão
Depois de tudo isso, vale resumir.
O Drizzle ORM é uma alternativa diferente no ecossistema de ORMs para TypeScript: em vez de esconder SQL, ele o adota. Seu núcleo de 7,4 kB, a ausência de dependências em runtime e o desempenho próximo ao SQL nativo mostram que ser leve não é abrir mão de recursos, mas escolher uma abordagem melhor.
Ele não substitui o Prisma em todos os casos. O Prisma tem um ecossistema mais maduro, ferramentas mais completas e uma experiência mais amigável para iniciantes. Mas o Drizzle pode ser a opção mais adequada quando o projeto:
- Roda em ambiente serverless.
- É sensível a desempenho e tamanho do bundle.
- Exige controle sobre SQL complexo.
- É desenvolvido por uma equipe com boa base de SQL.
Os dados da nossa equipe são claros: após migrar do Prisma para o Drizzle, o cold start ficou 73% mais rápido, o bundle diminuiu 78% e a primeira requisição caiu de três segundos para 700 ms. Isso não é uma micro-otimização; é uma mudança de patamar.
Se os cold starts e bundles grandes do Prisma estão causando problemas, experimente o Drizzle. A curva de aprendizado é curta: quem conhece SQL consegue começar em dez minutos.
Alguns links úteis:
- Documentação oficial do Drizzle ORM
- Repositório do Drizzle no GitHub
- Exemplo oficial de Next.js com Drizzle
- Comparação oficial entre Drizzle e Prisma
Experimente. Talvez, assim como eu, você não queira voltar atrás.
Configuração e migração completas de Next.js com Drizzle ORM
Configure o Drizzle ORM do zero, desde o ambiente, o schema e a conexão até as consultas e a migração gradual do Prisma
Estimated time: PT2H
-
1
Step 1: Primeira etapa: prepare o ambiente e instale as dependências
Crie o projeto Next.js: -
2
Step 2: Segunda etapa: defina o schema do banco
Crie db/schema.ts e defina as tabelas: -
3
Step 3: Terceira etapa: configure a conexão e o Drizzle Kit
Crie db/index.ts: -
4
Step 4: Quarta etapa: consulte com a API semelhante a SQL
Operações CRUD básicas: -
5
Step 5: Quinta etapa: use em Server Actions do Next.js
Crie app/actions.ts: -
6
Step 6: {user.name}
{user.email} -
7
Step 7: Sexta etapa: migre do Prisma para o Drizzle, opcional
Se o projeto já usa Prisma, faça uma migração gradual.
FAQ
Qual é a diferença entre Drizzle ORM e Prisma? Quando devo escolher o Drizzle?
Desempenho:
• Drizzle: 7,4 kB, cold start de 600 ms e nenhuma dependência em runtime
• Prisma v5: 14 MB, cold start de 2,5 s e binário Rust
• Prisma v7: 1 MB e cold start de 1,5 s; melhorou, mas ainda é mais lento que o Drizzle
Experiência de desenvolvimento:
• Drizzle: API semelhante a SQL, familiar para quem conhece SQL e sem geração de Client
• Prisma: DSL própria, que exige aprendizado e a execução de prisma generate
Escolha Drizzle quando:
✅ O projeto roda em Vercel, Cloudflare Workers, Deno Deploy ou outro ambiente serverless ou Edge
✅ O desempenho é crítico, como em aplicações em tempo real, sistemas financeiros e APIs de alta concorrência
✅ Há limite de bundle inferior a 1 MB
✅ A equipe domina SQL e prefere controlar as consultas diretamente
✅ O projeto exige otimizações SQL complexas
Escolha Prisma quando:
✅ A equipe não conhece bem SQL e precisa começar rápido
✅ A experiência de desenvolvimento e uma cadeia de ferramentas completa são prioridades
✅ É necessário administrar o banco visualmente com Prisma Studio
✅ Há consultas relacionais e modelagem de dados complexas
✅ O projeto não é serverless e o tamanho do bundle não é crítico
O que significa dizer que a API do Drizzle ORM é semelhante a SQL?
Exemplo em SQL:
```sql
SELECT * FROM posts
LEFT JOIN comments ON posts.id = comments.post_id
WHERE posts.id = 10
```
O equivalente em Drizzle:
```typescript
await db
.select()
.from(posts)
.leftJoin(comments, eq(posts.id, comments.postId))
.where(eq(posts.id, 10))
```
Características principais:
• A estrutura do código é praticamente igual à do SQL
• Quem conhece SQL entende a consulta rapidamente
• A curva de aprendizado é quase nula para quem já usa SQL
• Há inferência completa de tipos e preenchimento automático no IntelliSense
• Erros são detectados durante a compilação
Em comparação, o Prisma usa uma DSL própria:
```typescript
await prisma.user.findMany({
include: {
posts: {
where: { views: { gt: 1000 } },
},
},
});
```
Como o Drizzle fica mais próximo do SQL nativo, consultas complexas são mais fáceis de expressar.
Qual é a vantagem de desempenho do Drizzle ORM em ambientes serverless?
Melhorias de desempenho:
• Primeira requisição: de 3 s para 700 ms, 76% mais rápida
• Bundle de produção: de 18 MB para 4 MB, redução de 78%
• Cold start do Lambda: de 2,4 s para 650 ms, 73% mais rápido
Por quê?
1. Tamanho do bundle:
• O pacote principal do Drizzle ocupa apenas 7,4 kB com minificação e gzip
• O Client gerado pelo Prisma v5 pode chegar a 14 MB
• Em ambientes com limite de 1 MB, como Cloudflare Workers, o Prisma ultrapassa o teto
2. Cold start:
• Na inicialização, o Prisma precisa analisar uma enorme string DMMF, Data Model Meta Format
• Em um schema de porte médio, ela pode passar de seis milhões de caracteres
• Essa análise acontece a cada cold start e atrasa a inicialização
• O Drizzle não tem camada de abstração em runtime: a consulta é traduzida diretamente para SQL
3. Uso de memória:
• Drizzle: cerca de 5 MB
• Prisma v5: cerca de 80 MB
• Prisma v7: cerca de 30 MB, valor estimado
Esses números vêm do monitoramento de um ambiente real de produção, não de um benchmark artificial.
Como migrar do Prisma para o Drizzle? O processo é complicado?
Primeiro, converta o schema:
• Transforme os models do Prisma em definições pgTable
• Mapeie Int para serial ou integer, String para text e DateTime para timestamp
• Converta relações do Prisma para a função relations do Drizzle
Depois, reescreva as consultas:
• findUnique e findMany viram select().from().where()
• include vira leftJoin ou innerJoin
• create vira insert().values()
• update vira update().set().where()
Prisma e Drizzle podem coexistir durante a transição:
```typescript
// Use Drizzle em consultas sensíveis a desempenho
import { db } from '@/db/drizzle';
const latestPosts = await db
.select()
.from(posts)
.orderBy(desc(posts.createdAt))
.limit(50);
// Mantenha temporariamente o Prisma em relações complexas
import { prisma } from '@/db/prisma';
const userWithRelations = await prisma.user.findUnique({
where: { id: 1 },
include: {
posts: { include: { comments: { include: { author: true } } } },
},
});
```
Substitua as consultas aos poucos para reduzir o risco. O trabalho é principalmente uma conversão de sintaxe.
Quais bancos de dados o Drizzle ORM aceita e em quais ambientes ele roda?
• PostgreSQL, com recomendação para Neon Serverless
• MySQL
• SQLite, incluindo Turso e SQLite on the edge
• SQL Server
Ambientes serverless compatíveis:
• Vercel Edge Functions
• Cloudflare Workers
• AWS Lambda
• Deno Deploy
• Bun
Drivers serverless com suporte nativo:
• Neon Serverless para PostgreSQL
• PlanetScale para MySQL
• Turso para SQLite on the edge
• Supabase para PostgreSQL
Em nosso projeto, usamos Neon com Drizzle no Vercel Edge e reduzimos o cold start de 2,5 s para 700 ms.
Vantagens principais:
• Compatibilidade com os ambientes serverless mais usados
• Suporte nativo a drivers de banco serverless
• Nenhuma dependência em runtime, permitindo a execução em qualquer ambiente JavaScript
Como funciona a segurança de tipos no Drizzle ORM em comparação com o Prisma?
Inferência de tipos:
```typescript
// O tipo de retorno é inferido automaticamente
const users = await db.select().from(users);
// Tipo: { id: number; name: string; email: string; createdAt: Date }[]
// Ao selecionar campos, o tipo também é inferido
const result = await db
.select({
id: users.id,
name: users.name,
})
.from(users);
// Tipo: { id: number; name: string }[]
// O erro é detectado durante a compilação
await db.select().from(users).where(eq(users.id, '1'));
// ❌ Erro do TypeScript: string não pode ser atribuído a number
```
Características principais:
• Não é preciso gerar um Client; o TypeScript infere os tipos diretamente
• O IntelliSense completa campos, funções e operadores de condição
• Problemas aparecem na compilação, não apenas em runtime
• A verificação de tipos é rápida porque a inferência do Drizzle é mais simples
Em comparação com o Prisma:
• O Prisma exige prisma generate para criar o Client
• Em projetos grandes, a inferência do Prisma pode deixar a compilação TypeScript mais lenta
• A inferência mais simples do Drizzle tende a compilar mais rápido
Os tipos inferidos podem ser exportados para o frontend:
```typescript
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;
```
O Drizzle ORM é adequado para iniciantes? Como é a curva de aprendizado?
Tempo para começar:
• Quem conhece SQL: cerca de dez minutos
• Quem não conhece SQL: primeiro precisa aprender os fundamentos
Por que é fácil aprender:
• A estrutura da API é quase igual à do SQL
• Não há uma nova DSL para aprender, como no Prisma
• O IntelliSense oferece dicas completas de tipos e preenchimento automático
• O compilador TypeScript funciona como uma documentação prática
Exemplos:
```typescript
// SELECT
db.select().from(users).where(eq(users.id, 1))
// INSERT
db.insert(users).values({ name: 'John', email: '[email protected]' })
// UPDATE
db.update(users).set({ name: 'Jane' }).where(eq(users.id, 1))
// DELETE
db.delete(users).where(eq(users.id, 1))
```
Quem já usa SQL reconhece essas operações sem consultar a documentação.
Em comparação com o Prisma:
• O Prisma exige o aprendizado de uma DSL própria
• Ele é mais amigável para quem não conhece SQL
• Para quem já domina SQL, o Drizzle é mais direto
Recomendação:
• Se a equipe conhece SQL, escolha Drizzle diretamente
• Se a equipe não conhece SQL, use Prisma ou aprenda os fundamentos antes de adotar Drizzle
19 min de leitura · Publicado em: 20 dez 2025 · Atualizado em: 4 set 2026
Guia completo Next.js
Se você chegou pela busca, o caminho mais rápido é ir para o post anterior ou próximo desta série.
Anterior
Guia completo do SWR: estratégias de cache e atualizações otimistas na prática
Aprenda os conceitos centrais e as estratégias de cache do SWR e simplifique 90% do código de busca de dados com um único Hook. Inclui exemplos de atualização otimista, comparação com React Query e boas práticas de integração com Next.js.
Parte 50 de 51
Próximo
Este é o post mais recente da série até agora.



Comentários
Entre com GitHub para comentar