TypeScript ORM

Drizzle ORM

Drizzle é um ORM focado em TypeScript que mantém o SQL evidente. Defina tabelas em TypeScript, componha queries que espelham o SQL que elas geram e execute tudo em um runtime leve e compatível com edge.

intermediate14 min readUpdated 16 de set. de 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" }),
});
Lançado
2021
Licença
Apache-2.0
Linguagem
TypeScript
Runtime
Leve, sem engine em Rust ou codegen
Ferramenta de migração
drizzle-kit
Bancos de dados
PostgreSQL, MySQL, SQLite e variantes edge

Por que importa

Por que desenvolvedores escolhem o Drizzle

Queries que espelham o SQL

select, from, where, join e groupBy alinham-se um para um com a instrução que o Drizzle gera, então o código é lido como a própria query.

Um runtime minúsculo

Não há engine de query binária nem cliente gerado, portanto, uma função serverless carrega apenas o query builder e seu driver.

drizzle-kit para migrações

Gere migrações SQL revisáveis, aplique-as em ordem ou envie um schema diretamente para um banco de dados temporário durante a prototipagem.

O panorama completo

Três ideias por trás do Drizzle

Seu schema é TypeScript, suas queries parecem SQL e o banco de dados mantém todos os recursos nativos.

Schema em TypeScript

Definir

Tabelas são declaradas com builders tipados como pgTable, e métodos de coluna adicionam as mesmas restrições que você escreveria em DDL.

Queries com formato SQL

Consultar

O query builder compõe cláusulas em vez de escondê-las, e um template sql cobre qualquer coisa que o builder não exponha.

Seu banco de dados, inalterado

Conectar

Recursos específicos de cada dialeto permanecem disponíveis em vez de serem simplificados, para que PostgreSQL, MySQL e SQLite mantenham seus próprios pontos fortes.

Modelo de dados

A tabela de usuários, exatamente como escrita

Cada chamada do builder mapeia para uma coluna e restrição real. O banco de dados vê nomes em snake_case enquanto o código mantém camelCase.

A tabela de usuários, exatamente como escritaTabela PostgreSQL
  • iduuidChave primária; defaultRandom() torna-se gen_random_uuid()
  • emailtextNOT NULL com restrição UNIQUE
  • nametextNome de exibição anulável (nullable)
  • createdAttimestamptzNOT NULL, com padrão now() em UTC

Cada chamada do builder mapeia para uma coluna e restrição real. O banco de dados vê nomes em snake_case enquanto o código mantém camelCase.

Uma breve historia

Uma breve história de um ORM jovem

  1. 2021

    Drizzle torna-se open-source

    O projeto começa como um pequeno toolkit SQL em TypeScript em vez de um ORM completo.

    21
  2. 2022

    Chegada do drizzle-kit

    Uma CLI complementar adiciona geração de schema, migrações e introspecção de banco de dados.

    22
  3. 2023

    Foco em Serverless e Edge

    Drivers para Neon, PlanetScale, Turso e Cloudflare D1 tornam-no ideal para runtimes de edge.

    23
  4. 2024

    Maturação de queries relacionais

    A API db.query e o sistema de relações evoluem para uma forma nativa de carregar dados aninhados.

    24
  5. 2025

    Um padrão para bancos de dados edge

    Drizzle torna-se uma escolha comum onde bundles pequenos e cold starts rápidos são essenciais.

    25

O guia completo

Drizzle ORM: Tudo que voce precisa saber

O que é Drizzle?

Drizzle é um TypeScript ORM que trata o SQL como um cidadão de primeira classe. Seu schema é TypeScript comum, suas queries são construídas a partir de métodos que espelham as cláusulas SQL, e o runtime é leve o suficiente para rodar no edge. Não há etapa de geração de código nem engine binária: você importa drizzle-orm e faz a query.

Esse design reflete-se em cada API. db.select().from(users).where(eq(users.id, id)) é lido na mesma ordem que o SQL que ele produz. A biblioteca não esconde o banco de dados, ela o tipa. Se você já entende de joins, indexes e ON CONFLICT, você já entende a maior parte do Drizzle.

Drizzle suporta PostgreSQL, MySQL e SQLite, além de drivers serverless e edge como Neon, PlanetScale, Turso, Cloudflare D1 e o SQLite nativo do Bun. O mesmo estilo de schema e query funciona em todos eles, e é por isso que ele se tornou uma escolha comum para bancos de dados edge e serverless.

Seu schema é TypeScript

Não existe schema.prisma nem cliente gerado. Você define as tabelas com um builder por dialeto e as exporta.

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(),
});

O primeiro argumento é o nome da tabela no SQL, e cada builder de coluna recebe o nome da coluna. Esse mapeamento explícito permite que o banco de dados utilize snake_case enquanto seu código utiliza camelCase. Métodos de coluna como .notNull(), .unique() e .default() adicionam as mesmas constraints que você escreveria manualmente.

As relações são declaradas separadamente com relations(). Elas não criam colunas; elas informam à API de queries relacionais como as tabelas se conectam.

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],
  }),
}));

Tipagem gratuita

O Drizzle infere os tipos das linhas a partir do schema, então raramente você precisará escrevê-los manualmente. Cada tabela expõe $inferSelect e $inferInsert:

import { users } from "./schema";

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

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

Os helpers InferSelectModel e InferInsertModel fazem a mesma coisa e podem ser mais legíveis em algumas bases de código:

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

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

Como esses tipos vêm do mesmo objeto que constrói a query, a renomeação de uma coluna atualiza todos os consumidores simultaneamente. Esse é o benefício de manter o schema em TypeScript em vez de uma DSL separada: não existe um arquivo gerado que possa ficar dessincronizado.

Constraints e índices ficam ao lado das colunas

As regras ao nível de coluna abrangem NOT NULL, UNIQUE e valores padrão. Regras ao nível de tabela, como chaves compostas, índices e checks, ficam no terceiro argumento opcional, que retorna um 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')`),
  ],
);

Manter os índices no schema, em vez de em um script executado manualmente, significa que o drizzle-kit generate os cria junto com a tabela, e cada ambiente os recebe na mesma ordem. Se uma query estiver lenta, o índice de que ela precisa geralmente deve estar aqui, ao lado da coluna que ele cobre.

Conectando a um banco de dados

Crie uma conexão de driver, envolva-a com drizzle e passe o schema para que a API relacional consiga identificar suas relações.

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 });

Substitua node-postgres pelo driver que você realmente for implantar: drizzle-orm/neon-http para o endpoint HTTP do Neon, drizzle-orm/postgres-js, drizzle-orm/better-sqlite3, drizzle-orm/d1 ou drizzle-orm/bun-sqlite. A API de query acima de db é idêntica, que é exatamente o objetivo.

Drivers serverless e de edge

O motivo pelo qual o Drizzle funciona tão bem em runtimes de edge é que não há nada para empacotar além do driver. Neon over HTTP e Cloudflare D1 são os dois que você encontrará com mais frequência.

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);
  },
};

Crie o client uma vez por isolate, em vez de por requisição, e passe { schema } para que o db.query continue funcionando. Drivers HTTP e D1 podem limitar prepared statements ou transações dependendo do protocolo, portanto, verifique as notas do driver antes de depender de qualquer um deles em produção.

O query builder parece SQL

As queries são montadas através do encadeamento de métodos que mapeiam para cláusulas SQL. select, from, where, orderBy, limit e offset alinham-se perfeitamente com a instrução que geram.

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);

Os operadores são funções importadas em vez de símbolos: eq, ne, gt, gte, lt, lte, inArray, notInArray, like, ilike, isNull, isNotNull, e os combinadores and, or e not. Quando você precisa de algo que o builder não expõe, sql permite que você utilize SQL puro mantendo a parametrização.

import { sql } from "drizzle-orm";

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

Filtragem, ordenação e paginação

As condições são compostas com and, or e not, e as funções de operador cobrem os predicados usuais. Lê-las em conjunto é muito próximo de ler uma 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);

Isso é a paginação por keyset: em vez de offset, que faz com que o banco de dados escaneie e descarte linhas, você pagina a partir do último id visualizado. Para conjuntos de resultados menores, limit e offset funcionam perfeitamente, e para filtros anuláveis ou baseados em conjuntos, o mesmo builder se aplica:

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

A ordenação utiliza asc e desc em colunas, ou sql para expressões como desc(sqlcount(${posts.id})). Como cada cláusula é explícita, não há ordenação oculta ou filtro padrão para te surpreender.

Joins e agregados

Os joins são explícitos, exatamente como no SQL. Você escolhe entre innerJoin, leftJoin ou rightJoin e fornece a condição.

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);

Como você mesmo escreve o groupBy, não existe um plano de consulta oculto. Se o SQL estivesse errado, o TypeScript também estaria. Subqueries, CTEs com with, window functions e operações de conjunto estão todas disponíveis, seja através de helpers dedicados ou através de fragmentos sql.

A API de consultas relacionais

O query builder é preciso, porém verboso para o caso comum de carregar um usuário com seus posts. A API de consultas relacionais, db.query, assemelha-se mais ao 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 e findFirst compreendem o relations() que você declarou, e with os carrega em uma única viagem (round trip) por relação. As duas APIs compartilham o mesmo schema e podem ser misturadas em um único codebase: use o query builder quando precisar de controle, e db.query quando quiser um resultado aninhado sem precisar escrever o join.

Gravando dados

Inserts, updates e deletes são igualmente diretos. Adicione .returning() para recuperar as linhas afetadas em uma única instrução, evitando a necessidade de um 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 e onConflictDoUpdate mapeiam para ON CONFLICT para upserts. returning() é suportado no PostgreSQL, SQLite e MariaDB; o MySQL não oferece suporte, então você deve realizar um novo select. A API reflete deliberadamente o dialeto ao qual você está conectado, em vez de fingir que todos os bancos de dados são idênticos.

Transações

Transações são um callback que recebe um handle de transação. Toda query interna deve utilizar esse 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));
});

Lançar um erro (throw) dentro do callback reverte a transação (rollback). Chamadas aninhadas de tx.transaction tornam-se savepoints onde o dialeto as suporta, e você pode definir um nível de isolamento através do objeto de opções. Como em qualquer banco de dados, mantenha o callback curto e não utilize await para chamadas de rede não relacionadas dentro dele.

Migrations com drizzle-kit

drizzle-kit é a CLI complementar. Ela lê drizzle.config.ts e o schema, e gerencia as migrations.

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 o schema com o último snapshot, escreve um arquivo SQL numerado em out e registra um snapshot no journal. migrate aplica os arquivos pendentes em ordem. Ambos são seguros para produção porque o SQL é um artefato revisável que você commita.

push é o atalho para prototipagem: ele faz com que o banco de dados corresponda ao schema sem escrever arquivos de migration. É conveniente em desenvolvimento e arriscado em produção pelos mesmos motivos de qualquer comando de schema-push. pull faz o caminho inverso, fazendo a introspecção de um banco de dados existente para TypeScript, e studio abre um navegador de dados local. Para drivers apenas HTTP, como o Neon, use o migrador do driver em vez da CLI:

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

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

Prepared statements

O Drizzle pode preparar uma query uma única vez e executá-la diversas vezes com parâmetros diferentes, o que economiza o overhead de planejamento em caminhos críticos (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 });

Os placeholders são declarados com sql.placeholder("name") e fornecidos no momento da execução. Os prepared statements são nomeados para que o driver possa fazer o cache do plano; lembre-se que poolers de conexão em modo de transação, como o PgBouncer, podem não preservá-los entre diferentes conexões.

Um ORM, múltiplos bancos de dados

Como o query builder reflete o SQL, o Drizzle se adapta a cada dialeto em vez de simplificá-los. Você importa os construtores de colunas de drizzle-orm/pg-core, drizzle-orm/mysql-core ou drizzle-orm/sqlite-core, e os tipos e funções disponíveis seguem o banco de dados.

Isso significa que recursos específicos do PostgreSQL, como jsonb, arrays, pgEnum e colunas geradas, são tratados como cidadãos de primeira classe, enquanto MySQL e SQLite possuem seus próprios equivalentes. O custo disso é que mover um schema entre dialetos exige trabalho real: as abstrações são compartilhadas, mas o SQL não. Essa é uma escolha deliberada para equipes que preferem as capacidades reais do banco de dados em vez de um denominador comum mínimo.

Por que parece SQL

A maioria dos ORMs esconde o banco de dados e expõe objetos. O Drizzle faz o oposto: ele fornece tipos em torno do banco de dados que você já conhece. Disso decorrem três consequências.

Primeiro, o debugging é mais fácil. A query que você escreveu é a query que é executada, portanto, ler um log de lentidão ou uma saída de EXPLAIN mapeia diretamente de volta para o código. Segundo, o aprendizado é transferível, pois o conhecimento de joins, índices e ON CONFLICT aplica-se sem alterações. Terceiro, há menos “mágica” para te surpreender: sem proxies de lazy-loading que disparam queries quando você acessa uma propriedade, e sem identity map decidindo o que já está na memória.

O custo é que espera-se que você conheça SQL. O Drizzle gerará alegremente uma query ineficiente se você solicitar uma, e não irá te salvar de um índice ausente.

Performance e tamanho do bundle

O Drizzle não utiliza engine em Rust nem cliente gerado. O runtime é TypeScript puro com suporte a tree-shaking, portanto, uma serverless function ou edge worker carrega apenas o query builder e o driver. Não há um binário separado para fazer o bundle ou causar cold-start, o que é um dos principais motivos pelos quais as plataformas de edge o preferem.

No servidor, a história é a mesma de qualquer ORM: o banco de dados faz o trabalho pesado, e a forma como você estrutura suas queries define a performance. Use .returning() para evitar round trips extras, prepare queries frequentes, selecione apenas as colunas necessárias e deixe os índices fazerem o trabalho deles. Como nada é cacheado ou processado em lote (batched) sem o seu conhecimento, a performance que você mede é a performance do SQL que você escreveu.

Melhores práticas

  • Mantenha o schema.ts como a única fonte de verdade e exporte tabelas e relações de um único lugar.
  • Use drizzle-kit generate e faça o commit do SQL; nunca deixe o push tocar em produção.
  • Selecione apenas as colunas necessárias e adicione .returning() em vez de realizar uma leitura subsequente.
  • Prefira db.query com with para leituras aninhadas e o query builder para qualquer coisa personalizada.
  • Envolva escritas de múltiplas etapas em db.transaction e utilize o handle do tx em todo o processo.
  • Use placeholders sql e prepared statements em caminhos críticos (hot paths).
  • Escolha o driver adequado para a plataforma e compartilhe uma única instância de db por processo.
  • Adicione índices no schema ao lado das colunas que eles cobrem.
  • Execute as migrations no CI/CD antes do deploy do novo código.

Erros comuns

  • Executar drizzle-kit push contra um banco de dados de produção e remover colunas.
  • Usar o db externo dentro de uma transação em vez do tx fornecido.
  • Esquecer de passar { schema } para drizzle, o que quebra o db.query.
  • Escrever condições where com o operador errado, como eq onde inArray seria necessário.
  • Selecionar todas as colunas por padrão em caminhos críticos (hot paths).
  • Omitir índices em chaves estrangeiras usadas em joins.
  • Assumir que o MySQL suporta .returning().
  • Tratar fragmentos sql como automaticamente seguros quando interpolam strings de usuários.
  • Esquecer de executar generate após editar o schema e depois questionar por que a migration está vazia.

Próximos passos

O Drizzle valoriza o conhecimento do banco de dados subjacente, por isso, leia o guia de PostgreSQL para aprender sobre índices, transações e planejamento de consultas. Compare-o com o Prisma, que gera um cliente a partir de um schema, e com o TypeORM, que modela entidades como classes. Se você ainda está aprimorando seus conhecimentos na linguagem, o guia de TypeScript é a base na qual todos os três se apoiam.

Na pratica

Schema, join, query relacional, transação

Quatro abas cobrindo o schema, ambos os estilos de query e uma escrita 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],
  }),
}));

Query com formato SQL versus query de objeto

Drizzle mantém as cláusulas visíveis e retorna linhas tipadas; Prisma esconde o SQL atrás de um objeto e gera um cliente. Ambos são válidos, mas apenas um permite ler a query 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 versus push

generate escreve SQL versionado que você pode revisar e repetir. push faz o banco de dados corresponder ao schema sem histórico, o que é aceitável para protótipos, mas inseguro para produção.

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

Trade-offs

Drizzle é o ORM certo?

Drizzle troca a facilidade guiada por controle. Isso atende a equipes que conhecem SQL e valorizam um runtime pequeno, mas exige mais de quem não domina SQL.

Strengths

  • Você sempre sabe o SQL

    Queries mapeiam para cláusulas, então um log lento ou a saída de um EXPLAIN apontam diretamente para o código que a produziu.

  • Quase nenhum overhead de runtime

    Sem engine em Rust e sem geração de código significa bundles pequenos, cold starts rápidos e implantação confortável em runtimes de edge.

  • Tipos sem etapa de build

    Tipos de schema e query são inferidos de TypeScript comum, então não há comando de generate para esquecer no CI.

Trade-offs

  • Mais conhecimento de SQL necessário

    A biblioteca não impedirá você de escrever uma query ineficiente. Joins, índices e tratamento de conflitos dependem de você.

  • Relações são configuradas manualmente

    Você declara as relações separadamente e escolhe entre o query builder e db.query, o que é mais explícito, mas exige mais configuração.

  • Ecossistema mais jovem

    Existem menos plugins de terceiros e menos material extenso do que para ORMs mais antigos, embora o núcleo seja estável e bem documentado.

Perguntas frequentes

Perguntas frequentes

Keep learning

Related topics from the roadmap.

$ comecar a aprender

Pronto para aprender Drizzle ORM?

Nosso tutorial interativo te guia por Drizzle ORM passo a passo — com quizzes e codigo real que voce pode executar no navegador.