Guía práctica de Drizzle ORM: un ORM TypeScript 90% más ligero que Prisma

La semana pasada desplegué el proyecto en Vercel y la home tardó 3 segundos enteros en cargar. ¡3 segundos! Los usuarios ya se habían ido.
Abrí el análisis de bundle de Vercel y me quedé mirando el paquete Prisma Client de 14 MB, cuestionándome la vida. ¿Por qué cargar tanto código para consultas simples de usuarios? Peor aún: el cold start en Lambda era desesperante—sin Prisma, 600 ms; con Prisma, 2,5 s.
Dirás: «Prisma funciona muy bien». Yo también lo pensaba. Tipado seguro, migraciones automáticas, Prisma Studio… lo tiene todo. Pero el problema es que pesa demasiado.
Si te suena: cold starts lentos en serverless, bundles que se pasan de tamaño y consultas complejas que acaban en raw SQL, este artículo es para ti. Hoy presento Drizzle ORM: el núcleo pesa solo 7,4 kb, más del 90% menos que Prisma, con el mismo tipado seguro.
Te guiaré para configurar Next.js + Drizzle desde cero, explicaré la API SQL-like y compararé a fondo el rendimiento con Prisma. Sobre todo, cuándo elegir Drizzle y cuándo Prisma sigue siendo mejor opción.
¿Por qué Drizzle? Los dolores de los ORM actuales
Tres grandes dolores de Prisma
Prisma encaja bien en muchos escenarios. Pero con el uso, aparecen problemas difíciles de esquivar.
Dolor 1: el bundle se descontrola
El Client generado por Prisma v5 puede llegar a 14 MB. ¿Qué implica? Tu Next.js completo puede pesar 2-3 MB y Prisma se come casi la mitad. Prisma 7 lo bajó a ~1 MB (sin binario Rust), pero si sigues en versiones antiguas o el bundle importa mucho, es un problema serio.
Teníamos un chat en tiempo real en Cloudflare Workers con tope de 1 MB. Prisma lo superaba y tuvimos que cambiar.
Dolor 2: cold starts serverless lentos
En GitHub Issue #10724 llevan años debatiendo los cold starts lentos de Prisma en Lambda. Los datos son claros:
- Funciones sin ORM: ~600 ms
- Funciones con Prisma v5: ~2,5 s
- Funciones con Prisma v7: ~1,5 s (mejor, pero aún lento)
Prisma debe parsear al arrancar un DMMF (Data Model Meta Format) enorme. En schemas medianos puede superar 6 millones de caracteres. Cada cold start lo vuelve a parsear: difícil que vaya rápido.
Cal.com (calendario open source) publicó un post técnico sobre optimizar cold starts. Conclusión: el problema existe y solo se mitiga con workarounds.
Dolor 3: poco control sobre SQL
Prisma quiere «abstraer SQL» y que escribas con su DSL. Suele ir bien, pero en consultas complejas se complica.
Si necesitas JOINs múltiples, subconsultas y agregaciones condicionales, a veces la API de Prisma no basta y acabas en prisma.$queryRaw escribiendo SQL a mano.
Si al final escribes SQL, ¿por qué no empezar con una API SQL-like? Esa es la apuesta de Drizzle.
Lo que los desarrolladores necesitan de verdad
Resumiendo lo que buscan los desarrolladores:
- Tipado seguro sin sacrificar rendimiento: inferencia TypeScript sí, pero el ORM no debe frenar el proyecto
- SQL directo, sin DSL nuevo: SQL basta; no quiero otra sintaxis de consultas
- Compatible con serverless: en 2025 casi todos usamos Vercel, Cloudflare Workers o AWS Lambda
- Compilación rápida: en proyectos grandes, la inferencia de Prisma ralentiza TypeScript—un coste oculto
Drizzle nació para cubrir esto.
¿Qué es Drizzle ORM? Características clave
La filosofía de diseño de Drizzle
El lema de Drizzle es “If you know SQL, you know Drizzle”. Suena arrogante, pero cumple.
"If you know SQL, you know Drizzle."
Los ORM clásicos abstraen SQL. Drizzle hace lo contrario: no abstrae SQL; acerca la API TypeScript a la sintaxis SQL. El código se parece a SQL, con tipado completo.
Un ejemplo:
// Consulta Drizzle
await db
.select()
.from(posts)
.leftJoin(comments, eq(posts.id, comments.postId))
.where(eq(posts.id, 10))
// Generated SQL
SELECT * FROM posts
LEFT JOIN comments ON posts.id = comments.post_id
WHERE posts.id = 10
¿Ves? La estructura casi replica SQL. Quien sabe SQL lo entiende al instante.
Características principales
1. Ligereza extrema
El paquete drizzle-orm pesa solo 7,4 kb (min+gzip) y no tiene dependencias en runtime. Compara:
- Drizzle: ~7.4kb
- TypeORM: ~300kb
- Prisma v7: ~1MB
- Prisma v5: ~14MB
No están ni en el mismo orden de magnitud.
2. TypeScript primero, sin generar Client
Prisma exige prisma generate para el Client. Drizzle no.
Defines el schema y TypeScript infiere los tipos. IntelliSense, comprobación de tipos, avisos de error: todo en compilación, no en runtime.
3. API SQL-like con curva de aprendizaje casi nula
Si sabes SQL, arrancas con Drizzle en menos de 10 minutos.
// SELECT query
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))
Quien conoce SQL no necesita mirar la documentación.
4. Cero sobrecoste de rendimiento
Drizzle no añade capa de abstracción en runtime: las consultas van directo a SQL.
A diferencia de Prisma, que parsea DMMF al arrancar y mantiene estado interno, Drizzle es un constructor de consultas puro. Sin costes ocultos ni sorpresas.
5. Listo para serverless
Drizzle soporta los principales entornos serverless:
- Vercel Edge Functions
- Cloudflare Workers
- AWS Lambda
- Deno Deploy
- Bun
Y drivers serverless nativos:
- Neon Serverless
- PlanetScale
- Turso (SQLite on the edge)
- Supabase
Usamos Neon + Drizzle en Vercel Edge: el cold start bajó de 2,5 s a 700 ms. Efecto real.
Casos de uso
Drizzle no lo resuelve todo, pero encaja muy bien en:
1. Aplicaciones serverless
Si tu app corre en Lambda o Edge Functions, la ligereza y el arranque rápido de Drizzle son clave.
2. Escenarios sensibles al rendimiento
Apps en tiempo real, finanzas, analítica: cualquier escenario sensible a la latencia gana con el diseño sin abstracción de Drizzle.
3. Proyectos que exigen control SQL avanzado
Con muchas consultas complejas y optimización SQL manual, la API SQL-like de Drizzle supera al DSL de Prisma.
4. Proyectos full-stack con bundle limitado
Frameworks como SolidStart o Qwik empaquetan ORM en el cliente; ahí 7,4 kb marcan diferencia.
Al revés: si el equipo domina poco SQL, necesita prototipar rápido o valora Prisma Studio, Migrate o Pulse, Prisma puede seguir siendo mejor.
Configuración práctica: Next.js + Drizzle
Menos teoría, más práctica: configuraremos Next.js 15 + Drizzle + PostgreSQL desde cero.
Preparar el entorno
Crea el proyecto Next.js:
npx create-next-app@latest my-drizzle-app
cd my-drizzle-app
Instala las dependencias de Drizzle:
npm install drizzle-orm drizzle-kit
npm install @neondatabase/serverless # Si usas Neon
# o
npm install postgres # Si usas PostgreSQL tradicional
Recomiendo Neon: PostgreSQL serverless que encaja muy bien con Drizzle. Cuenta gratis, crea la base de datos y copia la cadena de conexión.
Definir el schema de la base de datos
Crea db/schema.ts y define las tablas:
import { pgTable, serial, text, timestamp, integer } from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';
// Users table
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
createdAt: timestamp('created_at').defaultNow(),
});
// Posts table
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(),
});
// Define relations (one-to-many)
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}));
Es TypeScript puro: sin schema especial de Prisma ni generar Client.
Configurar la conexión a la base de datos
Crea db/index.ts:
import { drizzle } from 'drizzle-orm/neon-http';
import { neon } from '@neondatabase/serverless';
import * as schema from './schema';
// Leer conexión desde variable de entorno
const sql = neon(process.env.DATABASE_URL!);
// Instancia Drizzle
export const db = drizzle(sql, { schema });
Así de simple. En .env.local:
DATABASE_URL=postgres://user:[email protected]/dbname
Configurar Drizzle Kit (herramienta de migraciones)
Crea 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!,
},
});
Generar archivos de migración:
npx drizzle-kit generate
Genera migraciones SQL en drizzle/. Revísalas y, si cuadra, ejecuta:
npx drizzle-kit push
Las tablas quedan creadas.
Estructura final del proyecto
my-drizzle-app/
├── app/ # Next.js application
│ ├── page.tsx
│ └── actions.ts # Server Actions
├── db/
│ ├── schema.ts # Table definitions and relations
│ └── index.ts # Database connection
├── drizzle/
│ └── migrations/ # Migration files (auto-generated)
├── drizzle.config.ts # Drizzle Kit configuration
├── .env.local # Environment variables
└── package.json
Todo el setup tarda menos de 5 minutos: sin schema Prisma complejo ni prisma generate largo, solo TypeScript.
API SQL-like de Drizzle en la práctica
Veamos consultas con Drizzle: operaciones habituales y uso en Next.js Server Actions.
Operaciones CRUD básicas
Consultar datos (SELECT)
import { db } from '@/db';
import { users, posts } from '@/db/schema';
import { eq, like, and, or, desc } from 'drizzle-orm';
// Consultar todos los usuarios
const allUsers = await db.select().from(users);
// Consultar un usuario
const user = await db.select().from(users).where(eq(users.id, 1));
// Búsqueda parcial
const result = await db.select().from(users).where(like(users.name, '%John%'));
// Condiciones compuestas
const admins = await db
.select()
.from(users)
.where(
and(
eq(users.role, 'admin'),
gt(users.createdAt, new Date('2024-01-01'))
)
);
// Ordenar y limitar
const latestPosts = await db
.select()
.from(posts)
.orderBy(desc(posts.createdAt))
.limit(10);
Es lógica SQL con llamadas a funciones.
Insertar datos (INSERT)
// Insertar un registro
await db.insert(users).values({
name: 'John Doe',
email: '[email protected]',
});
// Insertar varios registros
await db.insert(users).values([
{ name: 'Alice', email: '[email protected]' },
{ name: 'Bob', email: '[email protected]' },
]);
// Devolver lo insertado
const [newUser] = await db
.insert(users)
.values({ name: 'Charlie', email: '[email protected]' })
.returning();
console.log(newUser.id); // ID autogenerado
Actualizar datos (UPDATE)
// Actualizar un registro
await db
.update(users)
.set({ name: 'Jane Doe' })
.where(eq(users.id, 1));
// Actualización masiva
await db
.update(posts)
.set({ published: true })
.where(eq(posts.authorId, 1));
// Devolver lo actualizado
const [updatedUser] = await db
.update(users)
.set({ name: 'Updated Name' })
.where(eq(users.id, 1))
.returning();
Eliminar datos (DELETE)
// Eliminar un registro
await db.delete(users).where(eq(users.id, 1));
// Eliminación masiva
await db.delete(posts).where(eq(posts.published, false));
// Devolver lo eliminado
const deleted = await db
.delete(users)
.where(eq(users.id, 1))
.returning();
Consultas avanzadas
Operaciones JOIN
// LEFT JOIN: usuarios y sus 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: solo usuarios con posts
const activeAuthors = await db
.select()
.from(users)
.innerJoin(posts, eq(users.id, posts.authorId));
Subconsultas
// Usuarios con más de 5 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));
Funciones de agregación
import { count, sum, avg } from 'drizzle-orm';
// Contar usuarios totales
const [{ total }] = await db
.select({ total: count() })
.from(users);
// Contar posts por autor
const postCounts = await db
.select({
authorId: posts.authorId,
count: count(),
})
.from(posts)
.groupBy(posts.authorId);
Uso en Next.js Server Actions
Crea app/actions.ts:
'use server';
import { db } from '@/db';
import { users } from '@/db/schema';
import { eq } from 'drizzle-orm';
import { revalidatePath } from 'next/cache';
// Crear usuario
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: 'Error al crear usuario' };
}
}
// Obtener todos los usuarios
export async function getUsers() {
return await db.select().from(users);
}
// Eliminar usuario
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: 'Error al eliminar usuario' };
}
}
Uso en páginas:
// app/users/page.tsx
import { getUsers } from '../actions';
export default async function UsersPage() {
const users = await getUsers();
return (
<div>
<h1>Lista de usuarios</h1>
<ul>
{users.map(user => (
<li key={user.id}>
{user.name} - {user.email}
</li>
))}
</ul>
</div>
);
}
Seguridad de tipos en TypeScript
Aquí brilla Drizzle: inferencia de tipos completa.
// Tipo de retorno inferido automáticamente
const users = await db.select().from(users);
// Type: { id: number; name: string; email: string; createdAt: Date }[]
// Campos personalizados con tipos inferidos
const result = await db
.select({
id: users.id,
name: users.name,
})
.from(users);
// Type: { id: number; name: string }[]
// Errores detectados en compilación
await db.select().from(users).where(eq(users.id, '1'));
// ❌ Error TypeScript: el tipo 'string' no es asignable al tipo 'number'
IntelliSense sugiere campos, funciones y operadores. Escribir código es casi un juego: un punto y aparece todo.
No hace falta memorizar la API. El compilador TypeScript es la mejor documentación.
Drizzle vs Prisma: comparativa en profundidad
He alabado Drizzle, pero comparemos con objetividad para decidir qué encaja en tu proyecto.
Comparativa de rendimiento
| Dimensión | Drizzle | Prisma v5 | Prisma v7 | Notas |
|---|---|---|---|---|
| Tamaño del bundle | ~7,4 kb | ~14 MB | ~1 MB | Drizzle es el más ligero |
| Cold start | ~600 ms | ~2,5 s | ~1,5 s | Entorno serverless |
| Dependencias runtime | 0 | Binario Rust | 0 | Drizzle y Prisma v7 sin deps |
| Memoria | ~5 MB | ~80 MB | ~30 MB (est.) | Memoria en runtime |
| Velocidad de tipos | Rápida | Media | Media | Inferencia más simple en Drizzle |
Datos reales
Migré un proyecto de Prisma v5 a Drizzle:
- Primera petición: 3 s → 700 ms (76% más rápido)
- Bundle en producción: 18 MB → 4 MB (78% menos)
- Cold start Lambda: 2,4 s → 650 ms (73% más rápido)
Estas cifras no salen de un entorno de pruebas: son datos reales de monitorización en producción.
Comparativa de experiencia de desarrollo
Forma de definir el schema
// Drizzle (TypeScript code)
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
});
// Prisma (Dedicated DSL)
model User {
id Int @id @default(autoincrement())
name String
}
Ventaja Drizzle: TypeScript puro con buen soporte IDE; condicionales, genéricos, etc.
Ventaja Prisma: el schema Prisma es más conciso y legible
Estilo de la API de consultas
// Drizzle (SQL-like)
await db
.select()
.from(users)
.leftJoin(posts, eq(users.id, posts.authorId))
.where(gt(posts.views, 1000));
// Prisma (Chained API)
await prisma.user.findMany({
include: {
posts: {
where: { views: { gt: 1000 } },
},
},
});
Ventaja Drizzle: más cerca de SQL; consultas complejas más fáciles
Ventaja Prisma: más amigable sin SQL; consultas anidadas más claras
Comparativa de funcionalidades
Ventajas de Drizzle
- Muy ligero: núcleo de 7,4 kb, ideal si el bundle importa
- Nativo serverless: cold starts rápidos, perfecto para Edge
- Control SQL fuerte: más flexibilidad en consultas y optimización
- Sin generar Client: cambias schema y usas al momento, sin
prisma generate - Tree-shakable: solo empaquetas lo que usas
Ventajas de Prisma
- Ecosistema maduro: desde 2021, comunidad grande, muchos tutoriales
- Toolchain completa: Prisma Studio, Prisma Migrate, Prisma Pulse
- Consultas relacionales inteligentes: mitiga N+1, anidadas más intuitivas
- Amigable para principiantes: menos SQL profundo, DSL más suave
- Errores más claros: mensajes de runtime más útiles
Criterios de elección
Elige Drizzle cuando:
✅ Entornos Serverless/Edge (Vercel, Cloudflare Workers, Deno Deploy)
✅ Escenarios sensibles al rendimiento (apps en tiempo real, sistemas financieros, APIs de alta concurrencia)
✅ El bundle está limitado (entornos con tope < 1 MB)
✅ El equipo domina SQL y prefiere control directo de las consultas
✅ El proyecto requiere optimización SQL compleja
Elige Prisma cuando:
✅ El equipo domina poco SQL y necesita arrancar rápido
✅ Se valora la experiencia de desarrollo y una toolchain completa
✅ Se necesita gestión visual de la base de datos (Prisma Studio)
✅ Hay consultas relacionales complejas y modelado de datos avanzado
✅ El proyecto no es serverless y el tamaño del bundle no importa
Mi recomendación personal
En proyectos nuevos, yo haría así:
- Proyectos personales/startups: Drizzle (rendimiento, coste bajo, optimización máxima)
- Empresa/equipo: según SQL del equipo—fuerte → Drizzle; débil → Prisma
- Serverless-first: Drizzle sin dudarlo
- Servidor tradicional: ambos valen; Prisma tiene más herramientas
También puedes mezclar: Drizzle en módulos críticos, Prisma en admin. Conviven sin conflicto.
Guía de migración y buenas prácticas
Migrar de Prisma a Drizzle
Si ya tienes Prisma y quieres probar Drizzle, migra de forma progresiva.
Paso 1: conversión de schema
// Prisma Schema
model User {
id Int @id @default(autoincrement())
name String
email String @unique
posts Post[]
createdAt DateTime @default(now())
}
// Convertir a schema Drizzle
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
createdAt: timestamp('created_at').defaultNow(),
});
Paso 2: reescritura de consultas
// Consulta Prisma
const user = await prisma.user.findUnique({
where: { id: 1 },
include: { posts: true },
});
// Consulta Drizzle
const [user] = await db
.select()
.from(users)
.where(eq(users.id, 1))
.leftJoin(posts, eq(users.id, posts.authorId));
Paso 3: reemplazo progresivo
Puedes mantener Prisma y Drizzle a la vez:
// Consultas sensibles al rendimiento con Drizzle
import { db } from '@/db/drizzle';
const latestPosts = await db
.select()
.from(posts)
.orderBy(desc(posts.createdAt))
.limit(50);
// Consultas relacionales complejas: Prisma de forma temporal
import { prisma } from '@/db/prisma';
const userWithRelations = await prisma.user.findUnique({
where: { id: 1 },
include: {
posts: { include: { comments: { include: { author: true } } } },
},
});
Sustituye poco a poco para reducir riesgo.
Buenas prácticas con Drizzle
1. Gestión del pool de conexiones
En serverless, cuida el pool de conexiones:
import { drizzle } from 'drizzle-orm/neon-http';
import { neon, neonConfig } from '@neondatabase/serverless';
// Configurar pool de conexiones
neonConfig.fetchConnectionCache = true;
const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql, { schema });
2. Consultas preparadas
Consultas frecuentes: prepáralas para mejor rendimiento:
import { db } from '@/db';
import { users } from '@/db/schema';
import { eq } from 'drizzle-orm';
// Preparar consulta
const getUserById = db
.select()
.from(users)
.where(eq(users.id, placeholder('id')))
.prepare('get_user_by_id');
// Usar consulta preparada
const user = await getUserById.execute({ id: 1 });
3. Manejo de transacciones
await db.transaction(async (tx) => {
// Crear usuario
const [user] = await tx
.insert(users)
.values({ name: 'John', email: '[email protected]' })
.returning();
// Crear posts asociados
await tx.insert(posts).values({
title: 'First Post',
authorId: user.id,
});
// Si falla un paso, rollback total
});
4. Reutilizar tipos
Exporta tipos inferidos para el frontend:
// db/schema.ts
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull(),
});
// Exportar tipos inferidos
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;
// Uso en frontend
import type { User } from '@/db/schema';
function UserCard({ user }: { user: User }) {
return <div>{user.name}</div>;
}
5. Manejo de errores
import { db } from '@/db';
import { users } from '@/db/schema';
try {
await db.insert(users).values({
name: 'John',
email: '[email protected]',
});
} catch (error) {
// Códigos de error PostgreSQL
if (error.code === '23505') {
console.error('El correo ya existe');
} else {
console.error('Error de base de datos', error);
}
}
Conclusión
Para cerrar.
Drizzle ORM aporta aire fresco: en lugar de esconder SQL, lo abraza. Con 7,4 kb, cero dependencias en runtime y rendimiento casi nativo, demuestra que ligero no es compromiso, es mejor opción.
No sustituye del todo a Prisma, que tiene ecosistema y herramientas más maduros. Pero si tu proyecto:
- Corre en serverless
- Es sensible al rendimiento y al bundle
- Necesita control SQL avanzado
- Tiene equipo con base SQL
Drizzle puede encajar mejor.
Nuestros datos lo confirman: tras migrar, cold start 73% más rápido, bundle 78% menor, primera petición de 3 s a 700 ms. No es micro-optimización: es un salto cualitativo.
Si Prisma te frena por cold starts y bundles grandes, prueba Drizzle. Si sabes SQL, en 10 minutos arrancas.
Enlaces útiles:
- Documentación oficial de Drizzle ORM
- Repositorio GitHub de Drizzle
- Ejemplo oficial Next.js + Drizzle
- Comparativa oficial Drizzle vs Prisma
Pruébalo. Como a mí, puede que no quieras volver.
Guía completa de configuración y migración Next.js + Drizzle ORM
De cero a Drizzle ORM: entorno, schema, conexión, consultas y migración desde Prisma
Estimated time: PT2H
-
1
Step 1: Paso 1: entorno e instalación de dependencias
Crear proyecto Next.js: -
2
Step 2: Paso 2: definir schema
Crear db/schema.ts con las tablas: -
3
Step 3: Paso 3: conexión y Drizzle Kit
Crear db/index.ts: -
4
Step 4: Paso 4: API SQL-like
Operaciones CRUD básicas: -
5
Step 5: Paso 5: Server Actions en Next.js
Crear app/actions.ts: -
6
Step 6: {user.name}
{user.email} -
7
Step 7: Paso 6: migrar desde Prisma (opcional)
Migración progresiva si ya usas Prisma:
FAQ
¿Qué diferencia hay entre Drizzle ORM y Prisma? ¿Cuándo elegir Drizzle?
Rendimiento:
• Drizzle: 7.4kb, 600ms cold start, cero dependencias en runtime
• Prisma v5: 14MB, 2.5s cold start, requiere binario Rust
• Prisma v7: 1MB, 1.5s cold start, mejorado pero aún más lento que Drizzle
Experiencia de desarrollo:
• Drizzle: API SQL-like: si sabes SQL, sabes Drizzle; no hace falta generar Client
• Prisma: DSL propio, requiere aprendizaje y ejecutar prisma generate
Elige Drizzle cuando:
✅ Entornos Serverless/Edge (Vercel, Cloudflare Workers, Deno Deploy)
✅ Escenarios sensibles al rendimiento (apps en tiempo real, sistemas financieros, APIs de alta concurrencia)
✅ El bundle está limitado (entornos con tope < 1 MB)
✅ El equipo domina SQL y prefiere control directo de las consultas
✅ El proyecto requiere optimización SQL compleja
Elige Prisma cuando:
✅ El equipo domina poco SQL y necesita arrancar rápido
✅ Se valora la experiencia de desarrollo y una toolchain completa
✅ Se necesita gestión visual de la base de datos (Prisma Studio)
✅ Hay consultas relacionales complejas y modelado de datos avanzado
✅ El proyecto no es serverless y el tamaño del bundle no importa
¿Qué significa exactamente la API SQL-like de Drizzle ORM?
Comparación de ejemplo:
SQL:
```sql
SELECT * FROM posts
LEFT JOIN comments ON posts.id = comments.post_id
WHERE posts.id = 10
```
Drizzle:
```typescript
await db
.select()
.from(posts)
.leftJoin(comments, eq(posts.id, comments.postId))
.where(eq(posts.id, 10))
```
Características clave:
• La estructura del código casi replica SQL
• Quien sabe SQL lo entiende de un vistazo
• Curva de aprendizaje casi nula (si sabes SQL)
• Inferencia de tipos completa e IntelliSense
• Errores detectados en compilación
Comparado con el DSL de Prisma:
```typescript
// Prisma exige aprender una sintaxis de consulta nueva
await prisma.user.findMany({
include: {
posts: {
where: { views: { gt: 1000 } },
},
},
});
```
Drizzle está más cerca del SQL nativo; las consultas complejas son más fáciles de expresar.
¿Qué tan grandes son las ventajas de rendimiento de Drizzle ORM en entornos serverless?
Mejoras de rendimiento:
• Primera petición: 3 s → 700 ms (76% más rápido)
• Bundle en producción: 18 MB → 4 MB (78% menos)
• Cold start Lambda: 2,4 s → 650 ms (73% más rápido)
Análisis de causas:
1. Tamaño del bundle:
• El núcleo de Drizzle pesa solo 7,4 kb (min+gzip)
• El Client generado por Prisma v5 puede llegar a 14 MB
• En entornos con límite de 1 MB como Cloudflare Workers, Prisma supera el tope
2. Tiempo de cold start:
• Prisma debe parsear al arrancar un DMMF (Data Model Meta Format) enorme
• En schemas medianos puede superar 6 millones de caracteres
• Cada cold start debe parsearlo de nuevo, lo que ralentiza el arranque
• Drizzle no tiene capa de abstracción en runtime; las consultas se traducen directamente a SQL, sin costes ocultos
3. Uso de memoria:
• Drizzle: ~5MB
• Prisma v5: ~80MB
• Prisma v7: ~30 MB (estimado)
Estas cifras no salen de un entorno de pruebas: son datos reales de monitorización en producción.
¿Cómo migrar de Prisma a Drizzle? ¿Es complejo el proceso?
Paso 1: conversión de schema
Prisma Schema → Drizzle Schema:
• Los modelos Prisma pasan a definiciones pgTable
• Correspondencia de tipos: Int → serial/integer, String → text, DateTime → timestamp
• Las relaciones pasan de la sintaxis de asociación de Prisma a la función relations de Drizzle
Paso 2: reescritura de consultas
Consultas Prisma → consultas Drizzle:
• findUnique/findMany → select().from().where()
• include → leftJoin/innerJoin
• create → insert().values()
• update → update().set().where()
Paso 3: reemplazo progresivo
Puedes mantener Prisma y Drizzle a la vez:
```typescript
// Consultas sensibles al rendimiento con Drizzle
import { db } from '@/db/drizzle';
const latestPosts = await db
.select()
.from(posts)
.orderBy(desc(posts.createdAt))
.limit(50);
// Consultas relacionales complejas: Prisma de forma temporal
import { prisma } from '@/db/prisma';
const userWithRelations = await prisma.user.findUnique({
where: { id: 1 },
include: {
posts: { include: { comments: { include: { author: true } } } },
},
});
```
Sustituye poco a poco para reducir riesgo. La migración no es compleja: sobre todo conversión de sintaxis.
¿Qué bases de datos soporta Drizzle ORM? ¿En qué entornos puede ejecutarse?
• PostgreSQL (recomendado Neon Serverless)
• MySQL
• SQLite (incluye Turso, SQLite en el edge)
• SQL Server
Entornos serverless soportados:
• Vercel Edge Functions
• Cloudflare Workers
• AWS Lambda
• Deno Deploy
• Bun
Soporte nativo para drivers serverless:
• Neon Serverless (PostgreSQL)
• PlanetScale (MySQL)
• Turso (SQLite on the edge)
• Supabase (PostgreSQL)
Nuestro proyecto usa Neon + Drizzle desplegado en Vercel Edge; el cold start bajó de 2,5 s a 700 ms.
Ventajas clave:
• Soporta los principales entornos serverless
• Drivers serverless nativos
• Cero dependencias en runtime; funciona en cualquier entorno JavaScript
¿Cómo es la seguridad de tipos de Drizzle ORM? ¿Cómo se compara con Prisma?
Inferencia de tipos:
Drizzle (inferencia automática):
```typescript
// Tipo de retorno inferido automáticamente
const users = await db.select().from(users);
// Type: { id: number; name: string; email: string; createdAt: Date }[]
// Campos personalizados con tipos inferidos
const result = await db
.select({
id: users.id,
name: users.name,
})
.from(users);
// Type: { id: number; name: string }[]
// Errores detectados en compilación
await db.select().from(users).where(eq(users.id, '1'));
// ❌ Error TypeScript: el tipo 'string' no es asignable al tipo 'number'
```
Características clave:
• Sin generar Client; TypeScript infiere los tipos
• IntelliSense sugiere campos, funciones y operadores
• Errores en compilación, no en runtime
• Comprobación de tipos más rápida (inferencia más simple)
Comparado con Prisma:
• Prisma exige prisma generate para crear el Client
• En proyectos grandes, la inferencia de Prisma ralentiza la compilación TypeScript
• Drizzle infiere de forma más simple y compila más rápido
Exportar tipos para el frontend:
```typescript
// Exportar tipos inferidos
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;
```
¿Es Drizzle ORM adecuado para principiantes? ¿Cómo es la curva de aprendizaje?
Tiempo de arranque:
• Con SQL: arranque en 10 minutos
• Sin SQL: conviene aprender SQL antes
Por qué cuesta poco aprender:
• API SQL-like: estructura casi idéntica a SQL
• Sin aprender un DSL nuevo (como Prisma)
• Tipado completo e IntelliSense
• El compilador TypeScript es la mejor documentación
Ejemplo:
```typescript
// SELECT query
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))
```
Quien conoce SQL no necesita consultar documentación.
Comparado con Prisma:
• Prisma exige aprender su DSL
• Más amigable si no dominas SQL
• Si sabes SQL, Drizzle es más intuitivo
Recomendación:
• Equipo con buena base SQL: elige Drizzle
• Equipo sin SQL: Prisma, o aprende SQL y luego Drizzle
16 min de lectura · Publicado el: 20 dic 2025 · Actualizado el: 21 ago 2026
Guía completa de Next.js
Si llegaste desde búsqueda, lo más rápido es ir al artículo anterior o siguiente de esta misma serie.
Anterior
Guía completa de SWR: domina estrategias de caché y actualizaciones optimistas en la práctica
Aprende los conceptos clave y las estrategias de caché de SWR, y simplifica hasta el 90 % de tu código de obtención de datos con un solo Hook. Incluye ejemplos prácticos de actualizaciones optimistas, comparación con React Query y mejores prácticas de integración con Next.js.
Parte 50 de 51
Siguiente
Este es el artículo más reciente de la serie por ahora.



Comentarios
Inicia sesión con GitHub para dejar un comentario