TypeScript ORM

Drizzle ORM

Drizzle es un ORM orientado a TypeScript que mantiene el SQL a la vista. Define tablas en TypeScript, compone consultas que reflejan el SQL que generan y ejecútalo todo en un runtime ligero y optimizado para el edge.

intermediate14 min readUpdated 16 sept 2026
src/db/schema.ts
ts
// src/db/schema.ts
import { pgTable, text, uuid, timestamp } from "drizzle-orm/pg-core";

export const users = pgTable("users", {
  id: uuid("id").primaryKey().defaultRandom(),
  email: text("email").notNull().unique(),
  name: text("name"),
  createdAt: timestamp("created_at", { withTimezone: true })
    .notNull()
    .defaultNow(),
});

export const posts = pgTable("posts", {
  id: uuid("id").primaryKey().defaultRandom(),
  title: text("title").notNull(),
  authorId: uuid("author_id")
    .notNull()
    .references(() => users.id, { onDelete: "cascade" }),
});
Lanzado
2021
Licencia
Apache-2.0
Lenguaje
TypeScript
Runtime
Ligero, sin motor de Rust ni codegen
Herramienta de migración
drizzle-kit
Bases de datos
PostgreSQL, MySQL, SQLite y variantes de edge

Por que importa

Por qué los desarrolladores eligen Drizzle

Consultas que reflejan SQL

select, from, where, join y groupBy se alinean uno a uno con la sentencia que Drizzle genera, por lo que el código se lee como la consulta.

Un runtime diminuto

No hay un motor de consultas binario ni un cliente generado, por lo que una función serverless solo carga el constructor de consultas y su driver.

drizzle-kit para migraciones

Genera migraciones SQL revisables, aplícalas en orden o envía un esquema directamente a una base de datos temporal mientras prototipas.

La imagen completa

Tres ideas detrás de Drizzle

Tu esquema es TypeScript, tus consultas se leen como SQL y la base de datos conserva cada funcionalidad con la que fue lanzada.

Esquema en TypeScript

Definir

Las tablas se declaran con constructores tipados como pgTable, y los métodos de columna añaden las mismas restricciones que escribirías en DDL.

Consultas con forma de SQL

Consultar

El constructor de consultas compone cláusulas en lugar de ocultarlas, y una plantilla sql cubre cualquier cosa que el constructor no exponga.

Tu base de datos, sin cambios

Conectar

Las características específicas del dialecto permanecen disponibles en lugar de ser simplificadas, por lo que PostgreSQL, MySQL y SQLite conservan sus propias fortalezas.

Modelo de datos

La tabla de usuarios, exactamente como se escribe

Cada llamada al constructor se mapea a una columna y restricción real. La base de datos ve nombres en snake_case mientras que el código mantiene camelCase.

La tabla de usuarios, exactamente como se escribeTabla de PostgreSQL
  • iduuidClave primaria; defaultRandom() se convierte en gen_random_uuid()
  • emailtextNOT NULL con una restricción UNIQUE
  • nametextNombre visible nullable
  • createdAttimestamptzNOT NULL, con valor predeterminado now() en UTC

Cada llamada al constructor se mapea a una columna y restricción real. La base de datos ve nombres en snake_case mientras que el código mantiene camelCase.

Una breve historia

Una breve historia de un ORM joven

  1. 2021

    Drizzle se vuelve open-source

    El proyecto comienza como un pequeño toolkit de SQL para TypeScript en lugar de un ORM completo.

    21
  2. 2022

    Llega drizzle-kit

    Una CLI complementaria añade generación de esquemas, migraciones e introspección de base de datos.

    22
  3. 2023

    Prioridad en serverless y edge

    Los drivers para Neon, PlanetScale, Turso y Cloudflare D1 lo convierten en una opción natural para runtimes de edge.

    23
  4. 2024

    Madurez de las consultas relacionales

    La API db.query y el sistema de relaciones evolucionan hacia una forma de primer nivel para cargar datos anidados.

    24
  5. 2025

    Un estándar para bases de datos de edge

    Drizzle se convierte en una elección común donde importa un bundle pequeño y arranques en frío rápidos.

    25

La guia completa

Drizzle ORM: Todo lo que necesitas saber

¿Qué es Drizzle?

Drizzle es un ORM de TypeScript que trata a SQL como un ciudadano de primera clase. Tu esquema es TypeScript convencional, tus consultas se construyen a partir de métodos que reflejan las cláusulas de SQL y el runtime es lo suficientemente ligero como para ejecutarse en el edge. No hay pasos de generación de código ni motores binarios: simplemente importas drizzle-orm y realizas la consulta.

Ese diseño se refleja en cada API. db.select().from(users).where(eq(users.id, id)) se lee en el mismo orden que el SQL que produce. La librería no oculta la base de datos, sino que le asigna tipos. Si ya entiendes los joins, los índices y ON CONFLICT, ya entiendes la mayor parte de Drizzle.

Drizzle es compatible con PostgreSQL, MySQL y SQLite, además de drivers serverless y de edge como Neon, PlanetScale, Turso, Cloudflare D1 y el SQLite integrado de Bun. El mismo esquema y estilo de consulta se mantienen en todos ellos, razón por la cual se ha convertido en una opción común para bases de datos edge y serverless.

Tu esquema es TypeScript

No hay schema.prisma ni cliente generado. Defines las tablas con un builder por dialecto y las exportas.

import { pgTable, text, uuid, boolean, timestamp, integer } from "drizzle-orm/pg-core";

export const users = pgTable("users", {
  id: uuid("id").primaryKey().defaultRandom(),
  email: text("email").notNull().unique(),
  name: text("name"),
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
});

export const posts = pgTable("posts", {
  id: integer("id").primaryKey().generatedAlwaysAsIdentity(),
  title: text("title").notNull(),
  published: boolean("published").notNull().default(false),
  authorId: uuid("author_id")
    .notNull()
    .references(() => users.id, { onDelete: "cascade" }),
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
});

El primer argumento es el nombre de la tabla en SQL, y cada builder de columna recibe el nombre de la columna. Este mapeo explícito permite que la base de datos use snake_case mientras que tu código utiliza camelCase. Los métodos de columna como .notNull(), .unique() y .default() añaden las mismas restricciones que escribirías a mano.

Las relaciones se declaran por separado con relations(). Estas no crean columnas; le indican a la API de consultas relacionales cómo se conectan las tablas.

import { relations } from "drizzle-orm";

export const usersRelations = relations(users, ({ many }) => ({
  posts: many(posts),
}));

export const postsRelations = relations(posts, ({ one }) => ({
  author: one(users, {
    fields: [posts.authorId],
    references: [users.id],
  }),
}));

Los tipos vienen incluidos

Drizzle infiere los tipos de fila a partir del esquema, por lo que rara vez tendrás que escribir uno a mano. Cada tabla expone $inferSelect y $inferInsert:

import { users } from "./schema";

type User = typeof users.$inferSelect;
type NewUser = typeof users.$inferInsert;

function displayName(user: User) {
  return user.name ?? user.email;
}

Los helpers InferSelectModel y InferInsertModel hacen lo mismo y resultan más legibles en algunas bases de código:

import type { InferInsertModel, InferSelectModel } from "drizzle-orm";

type User = InferSelectModel<typeof users>;
type NewUser = InferInsertModel<typeof users>;

Debido a que estos tipos provienen del mismo objeto que construye la consulta, si renombras una columna, todos los consumidores se actualizan al instante. Esa es la ventaja de mantener el esquema en TypeScript en lugar de un DSL independiente: no hay archivos generados que puedan desincronizarse.

Las restricciones y los índices conviven con las columnas

Las reglas a nivel de columna cubren NOT NULL, UNIQUE y los valores por defecto. Las reglas a nivel de tabla, como las claves compuestas, los índices y los checks, van en el tercer argumento opcional, el cual devuelve un array.

import { sql } from "drizzle-orm";
import {
  check,
  index,
  pgTable,
  primaryKey,
  text,
  uniqueIndex,
  uuid,
} from "drizzle-orm/pg-core";

export const memberships = pgTable(
  "memberships",
  {
    userId: uuid("user_id").notNull().references(() => users.id),
    orgId: uuid("org_id").notNull(),
    role: text("role").notNull().default("member"),
  },
  (table) => [
    primaryKey({ columns: [table.userId, table.orgId] }),
    index("memberships_org_idx").on(table.orgId),
    uniqueIndex("memberships_user_org_idx").on(table.userId, table.orgId),
    check("memberships_role_check", sql`${table.role} in ('member', 'admin')`),
  ],
);

Mantener los índices en el esquema en lugar de en un script ejecutado manualmente significa que drizzle-kit generate los crea junto con la tabla, y cada entorno los recibe en el mismo orden. Si una consulta es lenta, el índice que necesita generalmente debe definirse aquí, junto a la columna que cubre.

Conexión a una base de datos

Crea una conexión con el driver, envuélvela con drizzle y pasa el esquema para que la API relacional pueda reconocer tus relaciones.

import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
import * as schema from "./schema";

const pool = new Pool({ connectionString: process.env.DATABASE_URL });

export const db = drizzle(pool, { schema });

Sustituye node-postgres por el driver que vayas a desplegar: drizzle-orm/neon-http para el endpoint HTTP de Neon, drizzle-orm/postgres-js, drizzle-orm/better-sqlite3, drizzle-orm/d1 o drizzle-orm/bun-sqlite. La API de consultas anterior db es idéntica, que es precisamente el objetivo.

Drivers para serverless y edge

La razón por la cual Drizzle se adapta tan bien a los entornos de edge es que no hay nada que empaquetar más allá del driver. Neon over HTTP y Cloudflare D1 son los dos que encontrarás con más frecuencia.

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 });
import { drizzle } from "drizzle-orm/d1";

export default {
  async fetch(_request: Request, env: Env) {
    const db = drizzle(env.DB);
    const rows = await db.select().from(users);
    return Response.json(rows);
  },
};

Crea el cliente una vez por isolate en lugar de hacerlo por cada solicitud, y pasa { schema } para que db.query siga funcionando. Los drivers de HTTP y D1 pueden limitar las prepared statements o las transacciones dependiendo del protocolo, así que revisa las notas del driver antes de depender de cualquiera de las dos en producción.

El query builder se lee como SQL

Las consultas se ensamblan encadenando métodos que se mapean a cláusulas de SQL. select, from, where, orderBy, limit y offset se alinean uno a uno con la sentencia que generan.

import { eq, desc, ilike, and } from "drizzle-orm";

const rows = await db
  .select({
    id: posts.id,
    title: posts.title,
    publishedAt: posts.createdAt,
  })
  .from(posts)
  .where(and(eq(posts.published, true), ilike(posts.title, "%drizzle%")))
  .orderBy(desc(posts.createdAt))
  .limit(20);

Los operadores son funciones importadas en lugar de símbolos: eq, ne, gt, gte, lt, lte, inArray, notInArray, like, ilike, isNull, isNotNull, y los combinadores and, or y not. Cuando necesites algo que el builder no exponga, sql te permite escribir SQL puro manteniendo la parametrización.

import { sql } from "drizzle-orm";

const active = await db
  .select()
  .from(users)
  .where(sql`${users.createdAt} > now() - interval '30 days'`);

Filtrado, ordenación y paginación

Las condiciones se componen con and, or y not, y las funciones de operador cubren los predicados habituales. Leerlas en conjunto es muy similar a leer una cláusula WHERE.

import { and, asc, gt, inArray, isNull, or } from "drizzle-orm";

const page = await db
  .select({ id: posts.id, title: posts.title, createdAt: posts.createdAt })
  .from(posts)
  .where(and(eq(posts.published, true), gt(posts.id, cursor)))
  .orderBy(asc(posts.id))
  .limit(20);

Esto es paginación basada en claves (keyset pagination): en lugar de usar offset, que obliga a la base de datos a escanear y descartar filas, paginas a partir del último id que viste. Para conjuntos de resultados más pequeños, limit y offset funcionan perfectamente, y para filtros basados en conjuntos o valores nulables se aplica el mismo builder:

const results = await db
  .select()
  .from(users)
  .where(
    or(
      isNull(users.name),
      inArray(users.email, ["[email protected]", "[email protected]"]),
    ),
  );

La ordenación utiliza asc y desc en las columnas, o sql para expresiones como desc(sqlcount(${posts.id})). Debido a que cada cláusula es explícita, no hay ordenaciones ocultas ni filtros predeterminados que puedan sorprenderte.

Joins y agregaciones

Los joins son explícitos, exactamente igual que en SQL. Eliges entre innerJoin, leftJoin o rightJoin y proporcionas la condición.

const report = await db
  .select({
    id: users.id,
    email: users.email,
    postCount: sql<number>`count(${posts.id})`.mapWith(Number),
  })
  .from(users)
  .leftJoin(posts, eq(posts.authorId, users.id))
  .where(eq(posts.published, true))
  .groupBy(users.id)
  .orderBy(desc(sql`count(${posts.id})`))
  .limit(20);

Dado que escribes el groupBy tú mismo, no hay un plan de consulta oculto. Si el SQL fuera incorrecto, el TypeScript también lo sería. Las subconsultas, los CTEs con with, las funciones de ventana y las operaciones de conjunto están disponibles, ya sea a través de helpers dedicados o mediante fragmentos de sql.

La API de consultas relacionales

El constructor de consultas es preciso, pero resulta demasiado verboso para el caso común de cargar un usuario con sus publicaciones. La API de consultas relacionales, db.query, se lee de forma más similar a Prisma:

const result = await db.query.users.findMany({
  columns: { id: true, email: true },
  with: {
    posts: {
      columns: { id: true, title: true },
      where: (posts, { eq }) => eq(posts.published, true),
      orderBy: (posts, { desc }) => [desc(posts.createdAt)],
      limit: 10,
    },
  },
});

db.query.users.findMany y findFirst entienden el relations() que declaraste, y with los carga en un solo viaje de ida y vuelta por relación. Ambas APIs comparten el mismo esquema y pueden mezclarse en un mismo proyecto: utiliza el constructor de consultas cuando necesites control, y db.query cuando quieras un resultado anidado sin tener que escribir el join.

Escritura de datos

Las inserciones, actualizaciones y eliminaciones son igual de directas. Añade .returning() para obtener las filas afectadas en una sola sentencia, lo que evita realizar un SELECT posterior.

await db
  .insert(users)
  .values({ email: "[email protected]", name: "Ada" })
  .returning();

await db
  .insert(posts)
  .values([
    { title: "First", authorId },
    { title: "Second", authorId, published: true },
  ])
  .onConflictDoNothing({ target: posts.id });

await db
  .update(users)
  .set({ name: "Ada Lovelace" })
  .where(eq(users.id, id))
  .returning();

await db.delete(posts).where(eq(posts.id, postId));

onConflictDoNothing y onConflictDoUpdate se mapean a ON CONFLICT para los upserts. returning() es compatible con PostgreSQL, SQLite y MariaDB; MySQL no lo soporta, por lo que en ese caso debes volver a seleccionar. La API refleja deliberadamente el dialecto al que estás conectado en lugar de pretender que todas las bases de datos son idénticas.

Transacciones

Las transacciones son un callback que recibe un handle de transacción. Cada consulta en su interior debe utilizar dicho handle.

await db.transaction(async (tx) => {
  await tx
    .update(accounts)
    .set({ balance: sql`${accounts.balance} - 5000` })
    .where(eq(accounts.id, from));

  await tx
    .update(accounts)
    .set({ balance: sql`${accounts.balance} + 5000` })
    .where(eq(accounts.id, to));
});

Lanzar un error dentro del callback revierte la transacción (rollback). Las llamadas anidadas a tx.transaction se convierten en savepoints en los dialectos que los soportan, y puedes definir un nivel de aislamiento a través del objeto de opciones. Como ocurre con cualquier base de datos, mantén el callback corto y no realices await de llamadas de red no relacionadas en su interior.

Migraciones con drizzle-kit

drizzle-kit es la CLI complementaria. Lee drizzle.config.ts y el esquema, y gestiona las migraciones.

import { defineConfig } from "drizzle-kit";

export default defineConfig({
  schema: "./src/db/schema.ts",
  out: "./drizzle",
  dialect: "postgresql",
  dbCredentials: { url: process.env.DATABASE_URL! },
});
pnpm drizzle-kit generate
pnpm drizzle-kit migrate
pnpm drizzle-kit push
pnpm drizzle-kit studio

generate compara el esquema con la última instantánea (snapshot), escribe un archivo SQL numerado en out y registra una instantánea en el diario. migrate aplica los archivos pendientes en orden. Ambos son seguros para producción porque el SQL es un artefacto revisable que puedes subir al repositorio.

push es el atajo para el prototipado: hace que la base de datos coincida con el esquema sin escribir archivos de migración. Es conveniente en desarrollo y arriesgado en producción por las mismas razones que cualquier comando de schema-push. pull hace lo contrario, realizando una introspección de una base de datos existente hacia TypeScript, y studio abre un navegador de datos local. Para drivers que solo soportan HTTP, como Neon, utiliza el migrador del driver en lugar de la CLI:

import { migrate } from "drizzle-orm/neon-http/migrator";

await migrate(db, { migrationsFolder: "./drizzle" });

Prepared statements

Drizzle puede preparar una consulta una sola vez y ejecutarla múltiples veces con diferentes parámetros, lo que ahorra la sobrecarga de planificación en rutas críticas (hot paths).

const getUser = db
  .select()
  .from(users)
  .where(eq(users.id, sql.placeholder("id")))
  .prepare("get_user");

const user = await getUser.execute({ id: userId });

Los marcadores de posición (placeholders) se declaran con sql.placeholder("name") y se proporcionan al momento de la ejecución. Los prepared statements tienen nombre para que el driver pueda cachear el plan; recuerda que los poolers de conexiones en modo transacción, como PgBouncer, podrían no preservarlos entre diferentes conexiones.

Un ORM, muchas bases de datos

Debido a que el query builder refleja el SQL, Drizzle se adapta a cada dialecto en lugar de simplificarlos. Importas los constructores de columnas desde drizzle-orm/pg-core, drizzle-orm/mysql-core o drizzle-orm/sqlite-core, y los tipos y funciones disponibles se ajustan según la base de datos.

Esto significa que las funcionalidades específicas de PostgreSQL, como jsonb, arrays, pgEnum y columnas generadas, son ciudadanos de primera clase, mientras que MySQL y SQLite tienen sus propios equivalentes. El coste es que migrar un esquema entre dialectos requiere trabajo real: las abstracciones se comparten, pero el SQL no. Este es un compromiso deliberado para los equipos que prefieren aprovechar las capacidades reales de la base de datos en lugar de limitarse al denominador común más bajo.

Por qué se siente como SQL

La mayoría de los ORM ocultan la base de datos y exponen objetos. Drizzle hace lo contrario: te proporciona tipos basados en la base de datos que ya conoces. De esto se derivan tres consecuencias.

Primero, la depuración es más sencilla. La consulta que escribiste es la consulta que se ejecuta, por lo que leer un log de consultas lentas o una salida de EXPLAIN se mapea directamente al código. Segundo, el aprendizaje es transferible, ya que los conocimientos sobre joins, índices y ON CONFLICT se aplican sin cambios. Tercero, hay menos “magia” que pueda sorprenderte: no hay proxies de lazy-loading que disparen consultas al acceder a una propiedad, ni un identity map que decida qué está ya en memoria.

El coste es que se espera que sepas SQL. Drizzle generará felizmente una consulta ineficiente si se lo pides, y no te salvará de la falta de un índice.

Rendimiento y tamaño del bundle

Drizzle no incluye ningún motor en Rust ni un cliente generado. El runtime es TypeScript puro que permite el tree-shaking, por lo que una serverless function o un edge worker solo cargan el query builder y el driver. No hay un binario separado que empaquetar o que cause cold-starts, razón principal por la cual las plataformas de edge lo prefieren.

En el servidor, la historia es la misma que con cualquier ORM: la base de datos hace el trabajo y el rendimiento depende de cómo estructures tus consultas. Usa .returning() para evitar viajes adicionales de ida y vuelta (round trips), prepara las consultas frecuentes, selecciona solo las columnas que necesites y deja que los índices hagan su trabajo. Debido a que nada se almacena en caché ni se agrupa (batching) a tus espaldas, el rendimiento que mides es el rendimiento del SQL que escribiste.

Mejores prácticas

  • Mantén schema.ts como la única fuente de verdad y exporta las tablas y relaciones desde un solo lugar.
  • Utiliza drizzle-kit generate y haz commit del SQL; nunca permitas que push toque producción.
  • Selecciona solo las columnas que necesites y añade .returning() en lugar de realizar una lectura posterior.
  • Prefiere db.query con with para lecturas anidadas y el query builder para cualquier cosa personalizada.
  • Envuelve las escrituras de múltiples pasos en db.transaction y utiliza el handle de tx en todo el proceso.
  • Usa marcadores de posición sql y sentencias preparadas (prepared statements) en los caminos críticos (hot paths).
  • Adapta el driver a la plataforma y comparte una única instancia de db por proceso.
  • Añade los índices en el esquema junto a las columnas que cubren.
  • Ejecuta las migraciones en CI/CD antes de desplegar el nuevo código.

Errores comunes

  • Ejecutar drizzle-kit push contra una base de datos de producción y eliminar columnas.
  • Usar el db externo dentro de una transacción en lugar del tx proporcionado.
  • Olvidar pasar { schema } a drizzle, lo que rompe db.query.
  • Escribir condiciones where con el operador incorrecto, como eq donde se necesita inArray.
  • Seleccionar todas las columnas por defecto en rutas críticas (hot paths).
  • Omitir índices en claves foráneas utilizadas en joins.
  • Asumir que MySQL soporta .returning().
  • Tratar los fragmentos sql como automáticamente seguros cuando interpolan strings de usuario.
  • Olvidar ejecutar generate después de editar el esquema y luego preguntarse por qué la migración está vacía.

Próximos pasos

Drizzle premia el conocimiento de la base de datos subyacente, así que lee la guía de PostgreSQL para aprender sobre índices, transacciones y planificación de consultas. Compáralo con Prisma, que genera un cliente a partir de un esquema, y con TypeORM, que modela las entidades como clases. Si aún estás profundizando en el lenguaje, la guía de TypeScript es la base en la que se apoyan los tres.

En la practica

Esquema, join, consulta relacional, transacción

Cuatro pestañas que cubren el esquema, ambos estilos de consulta y una escritura atómica.

src/db/schema.ts
import { relations } from "drizzle-orm";
import { pgTable, text, uuid, boolean, timestamp } from "drizzle-orm/pg-core";

export const users = pgTable("users", {
  id: uuid("id").primaryKey().defaultRandom(),
  email: text("email").notNull().unique(),
  name: text("name"),
  createdAt: timestamp("created_at", { withTimezone: true })
    .notNull()
    .defaultNow(),
});

export const posts = pgTable("posts", {
  id: uuid("id").primaryKey().defaultRandom(),
  title: text("title").notNull(),
  published: boolean("published").notNull().default(false),
  authorId: uuid("author_id")
    .notNull()
    .references(() => users.id, { onDelete: "cascade" }),
});

export const usersRelations = relations(users, ({ many }) => ({
  posts: many(posts),
}));

export const postsRelations = relations(posts, ({ one }) => ({
  author: one(users, {
    fields: [posts.authorId],
    references: [users.id],
  }),
}));

Consulta con forma de SQL frente a consulta de objeto

Drizzle mantiene las cláusulas visibles y devuelve filas tipadas; Prisma oculta el SQL detrás de un objeto y genera un cliente. Ambos son válidos, pero solo uno te permite leer la consulta como SQL.

Drizzle
const rows = await db
  .select({ id: users.id, email: users.email })
  .from(users)
  .where(eq(users.email, email))
  .limit(1);
Prisma
const rows = await prisma.user.findMany({
  where: { email },
  select: { id: true, email: true },
  take: 1,
});

Generate frente a push

generate escribe SQL versionado que puedes revisar y repetir. push hace que la base de datos coincida con el esquema sin historial, lo cual es útil para un prototipo pero inseguro para producción.

Preferir
pnpm drizzle-kit generate
pnpm drizzle-kit migrate
Evitar
# No files, no journal, no audit trail.
pnpm drizzle-kit push

Compromisos

¿Es Drizzle el ORM adecuado?

Drizzle cambia la asistencia guiada por el control. Esto es ideal para equipos que conocen SQL y valoran un runtime pequeño, pero exige más de los demás.

Strengths

  • Siempre conoces el SQL

    Las consultas se mapean a cláusulas, por lo que un log lento o una salida de EXPLAIN apunta directamente al código que lo produjo.

  • Casi sin sobrecarga de runtime

    Sin motor de Rust ni generación de código, lo que significa bundles pequeños, arranques en frío rápidos y despliegues cómodos en runtimes de edge.

  • Tipos sin paso de compilación

    Los tipos de esquema y consulta se infieren de TypeScript ordinario, por lo que no hay un comando generate que olvidar en CI.

Trade-offs

  • Se requiere más conocimiento de SQL

    La librería no te impedirá escribir una consulta ineficiente. Los joins, índices y el manejo de conflictos dependen de ti.

  • Las relaciones se configuran a mano

    Declaras las relaciones por separado y eliges entre el constructor de consultas y db.query, lo cual es más explícito pero requiere más configuración.

  • Un ecosistema más joven

    Hay menos plugins de terceros y menos material extenso que en ORMs más antiguos, aunque el núcleo es estable y está bien documentado.

Preguntas frecuentes

Preguntas frecuentes

Keep learning

Related topics from the roadmap.

$ comienza a aprender

Listo para aprender Drizzle ORM?

Nuestro tutorial interactivo te guia a traves de Drizzle ORM paso a paso — con quizzes y codigo real que puedes ejecutar en el navegador.