Drizzle ORM com Next.js 16: Guia Prático de Integração em Server Components
Guia prático para integrar Drizzle ORM no Next.js 16 com Server Components, Server Actions, Edge runtime, connection pooling e o novo cache explícito. Padrões de produção testados em projetos reais.
O Drizzle ORM com Next.js 16 é hoje a combinação mais leve e type-safe para acessar bancos relacionais direto de Server Components, com SQL-like em TypeScript, suporte nativo ao Edge runtime e migrações declarativas via Drizzle Kit. Diferente de ORMs baseados em runtime pesado, o Drizzle compila para SQL puro e roda dentro de funções serverless, edge functions e até no novo modelo de cache do Next.js 16. Neste guia prático você vai ver o setup completo, queries em Server Components, mutações com Server Actions, pooling de conexões e padrões de produção testados em projetos reais.
Drizzle ORM 0.36+ é totalmente compatível com Next.js 16 e React 19, incluindo Server Components assíncronos e o novo cache explícito.
Use drizzle-orm/neon-http ou drizzle-orm/postgres-js conforme o driver. O primeiro funciona em Edge runtime sem ajustes.
Drizzle Kit 0.28 gera migrações SQL legíveis a partir do schema TypeScript com drizzle-kit generate e aplica com drizzle-kit migrate.
Combine queries Drizzle com "use cache" e cacheTag para revalidação granular sem perder type-safety.
Connection pooling em serverless exige driver HTTP (Neon, Vercel Postgres) ou um pooler externo como PgBouncer/Supavisor em modo transação.
Server Actions devem chamar revalidateTag após mutações para sincronizar o cache do Next.js com o estado do banco.
Por que Drizzle com Next.js 16?
O Next.js 16 consolidou o App Router como padrão e tornou os Server Components assíncronos o lugar natural para buscar dados. Nesse cenário, um ORM precisa de três qualidades raras juntas: ser type-safe sem gerar clientes pesados, rodar tanto em Node quanto em Edge runtime, e não bloquear o streaming de Suspense. O Drizzle ORM entrega exatamente isso, porque é uma camada fina sobre SQL. Você escreve TypeScript que se parece com SQL e o Drizzle emite a query nativa, sem proxies dinâmicos nem geração de cliente em tempo de build.
Honestamente, esse design tem implicações concretas em produção. Um Server Component pode importar db diretamente, executar await db.select().from(users) e o Next.js 16 deduplica essa chamada dentro da mesma request usando o cache de request scope do React. Como o Drizzle não mantém um worker process próprio nem schemas em memória, a inicialização cold start em funções edge fica abaixo de 50ms na maioria dos casos, contra 300 a 500ms de ORMs mais pesados. Em uma migração que fiz no início do ano, esse ajuste sozinho derrubou o p95 da home em quase 200ms na Vercel.
O ecossistema também amadureceu bastante: Drizzle Kit para migrações, Drizzle Studio como GUI local, suporte oficial a PostgreSQL, MySQL, SQLite, Neon, PlanetScale, Turso, D1 e Supabase. Se você já segue o nosso guia de Server Actions no Next.js, integrar Drizzle nas mutations é a evolução natural.
Drizzle vs Prisma: qual escolher em 2026?
A pergunta mais frequente em comunidades Next.js é se vale migrar do Prisma para o Drizzle. Resposta curta: depende do tamanho do projeto e do runtime alvo. Para edge functions e projetos serverless com cold starts frequentes, o Drizzle ganha por margem larga. Já para projetos monolíticos com Node em servidor dedicado e equipe acostumada com generators, o Prisma 6 continua excelente.
Critério
Drizzle ORM 0.36
Prisma 6
Tamanho do bundle
~7 KB (core)
~2 MB (client gerado)
Cold start em Edge
30 a 60 ms
Não suportado nativamente*
Type-safety
Inferência direta do schema TS
Cliente gerado a partir de .prisma
Sintaxe
SQL-like fluente
API de alto nível
Migrações
Drizzle Kit (SQL legível)
prisma migrate (SQL gerado)
Curva de aprendizado
Média (requer conhecer SQL)
Baixa (abstrai SQL)
Suporte a raw SQL
Nativo via sql tag
Via $queryRaw
*O Prisma suporta Edge com o Accelerate (serviço pago) ou driver adapters experimentais. Para a maioria dos projetos novos em Next.js 16, eu recomendo Drizzle quando o time já lê SQL com fluência, e Prisma quando a prioridade é onboarding rápido de devs juniores.
Setup inicial do Drizzle em um projeto Next.js 16
Vamos começar do zero. Crie um projeto Next.js 16 e adicione as dependências do Drizzle. Vou usar PostgreSQL via Neon como exemplo porque é o stack mais comum em deploys Vercel, mas os mesmos padrões valem para Supabase, Turso ou Postgres self-hosted.
Por fim, instancie o cliente em src/db/index.ts. Para Edge runtime use neon-http. Para Node runtime tradicional, prefira postgres-js com pool nativo.
import { neon } from "@neondatabase/serverless";
import { drizzle } from "drizzle-orm/neon-http";
import * as schema from "./schema";
const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql, { schema });
Schema, Drizzle Kit e migrações declarativas
O schema em Drizzle é TypeScript puro. Você descreve tabelas como objetos e as constraints como funções helper. Isso significa que o autocomplete do editor já te guia desde o primeiro caractere, e qualquer mudança no schema faz o build falhar se houver query incompatível.
Gerar e aplicar a primeira migração leva dois comandos:
npx drizzle-kit generate
npx drizzle-kit migrate
O comando generate cria um arquivo SQL em ./drizzle/0000_xxx.sql com o DDL completo. Recomendo versionar esse SQL. Em code review você lê exatamente o que vai bater no banco. Para produção na Vercel, rode drizzle-kit migrate em um script de build ou em um GitHub Action separado antes do deploy, nunca como parte da build do Next.js (a build pode rodar em ambiente sem acesso ao banco). Vale conferir a documentação oficial de migrações do Drizzle para padrões de CI/CD.
Queries type-safe em Server Components
Server Components são o ponto onde o Drizzle brilha. Como o componente é renderizado no servidor, você pode importar db diretamente e fazer a query inline, sem API route intermediária, sem fetch, sem JSON serialization manual.
// src/app/posts/page.tsx
import { db } from "@/db";
import { posts, users } from "@/db/schema";
import { desc, eq } from "drizzle-orm";
import Link from "next/link";
export default async function PostsPage() {
const rows = await db
.select({
id: posts.id,
title: posts.title,
authorName: users.name,
createdAt: posts.createdAt,
})
.from(posts)
.leftJoin(users, eq(posts.authorId, users.id))
.where(eq(posts.published, true))
.orderBy(desc(posts.createdAt))
.limit(20);
return (
<ul>
{rows.map((row) => (
<li key={row.id}>
<Link href={`/posts/${row.id}`}>{row.title}</Link>
<span> por {row.authorName}</span>
</li>
))}
</ul>
);
}
Repare que rows tem tipo totalmente inferido. Se você renomear title no schema, o TypeScript reclama imediatamente. Para queries com relacionamentos, a Relational Queries API é ainda mais ergonômica:
Para inserts, updates e deletes, Server Actions são o caminho idiomático no Next.js 16. Combinadas com Drizzle, você ganha validação de tipos do payload até a query SQL final. Veja um exemplo completo de criação de post com revalidação de cache:
// src/app/posts/actions.ts
"use server";
import { db } from "@/db";
import { posts } from "@/db/schema";
import { revalidateTag } from "next/cache";
import { redirect } from "next/navigation";
import { z } from "zod";
const CreatePostSchema = z.object({
title: z.string().min(3).max(200),
content: z.string().min(1),
authorId: z.coerce.number().int().positive(),
});
export async function createPost(formData: FormData) {
const parsed = CreatePostSchema.safeParse({
title: formData.get("title"),
content: formData.get("content"),
authorId: formData.get("authorId"),
});
if (!parsed.success) {
return { error: parsed.error.flatten().fieldErrors };
}
const [novoPost] = await db
.insert(posts)
.values({ ...parsed.data, published: true })
.returning({ id: posts.id });
revalidateTag("posts");
redirect(`/posts/${novoPost.id}`);
}
Repare em três detalhes que evitam bugs em produção: validação com Zod antes de tocar o banco, .returning() para recuperar o ID gerado em uma única roundtrip, e revalidateTag para invalidar o cache da listagem. Se você ainda não se familiarizou com Server Actions, dá uma olhada no guia completo de Server Actions no Next.js.
Edge runtime e connection pooling em serverless
Rodar Drizzle em Edge runtime exige um driver HTTP-based, porque Edge functions não suportam TCP de longa duração. O driver @neondatabase/serverless resolve isso usando HTTPS por baixo dos panos. Cada query é uma requisição HTTP independente, sem pool stateful.
// src/app/api/health/route.ts
export const runtime = "edge";
import { db } from "@/db";
import { sql } from "drizzle-orm";
export async function GET() {
const result = await db.execute(sql`SELECT 1 as ok`);
return Response.json({ ok: result.rows[0].ok === 1 });
}
Para Postgres tradicional (RDS, Supabase com porta 5432), você precisa de um pooler externo. As opções para 2026:
Supavisor (Supabase): modo transaction para serverless, modo session para apps long-running.
PgBouncer: clássico, exige a flag ?pgbouncer=true e desabilitar prepared statements em alguns ORMs.
Em Node runtime com postgres-js, configure o cliente para reaproveitar conexões entre invocações warm da função:
import postgres from "postgres";
import { drizzle } from "drizzle-orm/postgres-js";
const queryClient = postgres(process.env.DATABASE_URL!, {
max: 1,
idle_timeout: 20,
connect_timeout: 10,
});
export const db = drizzle(queryClient);
O max: 1 parece contraintuitivo, mas é o correto em serverless. Cada invocação warm reusa uma única conexão, e o pool real fica no pooler externo. Vale ler a recomendação oficial da Vercel sobre pooling em functions para os detalhes.
Caching com a diretiva "use cache" e tags
O Next.js 16 trouxe o modelo de cache explícito com a diretiva "use cache". Combinada com Drizzle, dá pra cachear queries inteiras e revalidar por tag quando uma mutação acontece. Esse é o padrão recomendado pelo time do Next.js para 2026.
// src/db/queries.ts
import { db } from "@/db";
import { posts } from "@/db/schema";
import { cacheTag } from "next/cache";
import { eq, desc } from "drizzle-orm";
export async function getPublishedPosts() {
"use cache";
cacheTag("posts");
return db.query.posts.findMany({
where: eq(posts.published, true),
orderBy: desc(posts.createdAt),
limit: 20,
});
}
Para entender em profundidade como o cache explícito interage com o roteador, recomendo o nosso guia completo de Cache Components e Partial Prerendering no Next.js 16. Regra prática: cada função cacheada deve ter pelo menos um cacheTag estável, e cada Server Action que altera dados correspondentes deve chamar revalidateTag com a mesma string.
Erros comuns e como resolvê-los
Listei aqui os problemas que mais aparecem em issues do GitHub e no Discord do Drizzle quando se integra com Next.js 16. Quase todos eu já enfrentei pessoalmente em algum projeto.
"Cannot find module 'drizzle-orm/neon-http'"
Atualize o pacote para 0.36 ou superior. Versões antigas não exportavam o subpath. Rode npm install drizzle-orm@latest.
"Too many connections" em produção
Você está usando o endpoint não-pooled. Troque a connection string para a versão terminada em -pooler ou aponte para PgBouncer/Supavisor em modo transaction. Em postgres-js, force max: 1.
Migrações conflitam em monorepos
Verifique que apenas um pacote contém o schema canônico. O Drizzle Kit não detecta schemas duplicados, ele simplesmente regenera tudo. Centralize em um pacote @meu-app/db e importe nos demais.
Build da Vercel falha com "DATABASE_URL is undefined"
O Next.js 16 trata env vars de runtime separadamente das de build. Garanta que DATABASE_URL está marcado como disponível em build settings, ou use process.env.DATABASE_URL ?? "" no drizzle.config.ts com fallback que não quebra a build (apenas a migração falha, e migrações não devem rodar na build mesmo).
Hydration mismatch após mutation
Você esqueceu de revalidateTag ou revalidatePath após a Server Action. O cliente vê dados antigos do cache enquanto o servidor já tem os novos. Sempre revalide antes do redirect.
Perguntas frequentes
Drizzle ORM funciona com Next.js 16 e React 19?
Sim. O Drizzle 0.36+ é totalmente compatível com Next.js 16, React 19 e Server Components assíncronos. Não há configuração especial necessária além de escolher o driver correto (neon-http para Edge, postgres-js para Node).
Drizzle é mais rápido que Prisma no Next.js?
Em cold starts e Edge runtime, sim. O Drizzle inicia em 30 a 60ms contra 300 a 500ms do Prisma. Em queries quentes, a diferença é desprezível porque ambos delegam para o driver nativo do banco.
Posso usar Drizzle em Vercel Edge Functions?
Sim, desde que use um driver HTTP-based como @neondatabase/serverless, @planetscale/database ou @libsql/client para Turso. Drivers TCP tradicionais como pg não funcionam em Edge.
Como faço connection pooling com Drizzle em serverless?
Use um pooler externo (PgBouncer, Supavisor, Neon Pooled) e configure o driver com max: 1. Cada função serverless mantém uma conexão e o pooler gerencia o resto.
Drizzle Kit gera SQL ou eu preciso escrever migrações manualmente?
O drizzle-kit generate gera arquivos SQL automaticamente comparando seu schema TypeScript com o estado anterior. Você pode revisar e editar o SQL antes de aplicar com drizzle-kit migrate.
Rotas paralelas e interceptadas no Next.js App Router permitem dashboards com painéis independentes e modais com URL própria. Guia completo com código.
O Next.js 16 trocou middleware.ts por proxy.ts, migrando do Edge Runtime para Node.js. Aprenda a migrar com codemod, integrar Auth.js e next-intl, e evitar os erros mais comuns.
Domine Server Actions no Next.js: desde formulários com progressive enhancement até padrões avançados como useActionState, useOptimistic, validação com Zod e segurança de endpoints públicos.