Drizzle ORM — The Lightweight TypeScript ORM Reshaping How We Write SQL

Posted on: 4/27/2026 7:15:16 AM

~12KBBundle size (130x smaller than Prisma)
0Dependencies
50msCold start on Lambda
30K+GitHub Stars

1. What Is Drizzle ORM?

Drizzle ORM is a SQL-first TypeScript ORM — meaning you write schemas and queries that closely mirror raw SQL, wrapped in TypeScript with full type safety. There's no separate query engine, no Rust binary, no code generation step — everything is pure TypeScript from start to finish.

While Prisma abstracts SQL behind a custom DSL (.prisma schema files), Drizzle takes the opposite approach: keeping the SQL mental model intact while adding type safety and a modern developer experience. The philosophy: "If you know SQL, you already know Drizzle."

Why Drizzle Rose to Prominence in 2026

The explosion of edge computing (Cloudflare Workers, Vercel Edge Functions) created strict requirements around bundle size and cold start times. Prisma 5.x with its ~13MB Rust query engine was nearly impossible to deploy on the edge. Drizzle with its ~12KB runtime became the natural choice for edge-first applications.

2. Architecture: Why Is Drizzle So Small?

graph TB
    subgraph Prisma["Prisma Architecture"]
        PA["Schema DSL
(.prisma file)"] -->|"prisma generate"| PB["Generated Client
+ Types"] PB --> PC["TypeScript/WASM
Query Engine ~1.6MB"] PC --> PD["Database Driver"] end subgraph Drizzle["Drizzle Architecture"] DA["TypeScript Schema
(tables, columns)"] -->|"Direct import"| DB["Query Builder
~12KB total"] DB --> DC["Database Driver"] end PD --> E["PostgreSQL / MySQL
SQLite / SQL Server"] DC --> E style PA fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50 style PB fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50 style PC fill:#ff9800,stroke:#fff,color:#fff style PD fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50 style DA fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style DB fill:#e94560,stroke:#fff,color:#fff style DC fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style E fill:#2c3e50,stroke:#fff,color:#fff
Prisma adds a Query Engine layer; Drizzle generates SQL directly from TypeScript

Drizzle eliminates the middleware layer entirely. Schemas are defined using pure TypeScript — pgTable(), mysqlTable(), sqliteTable() — and the query builder reads this schema to produce SQL strings directly. No generate step, no intermediate files, types always stay in sync with source code.

3. Schema Definition — Code-First in TypeScript

One of Drizzle's strongest points: the schema is real TypeScript code, not a custom DSL.

// schema.ts — Drizzle schema definition
import { pgTable, serial, text, integer, timestamp, boolean } from 'drizzle-orm/pg-core';

export const users = pgTable('users', {
  id: serial('id').primaryKey(),
  email: text('email').notNull().unique(),
  name: text('name').notNull(),
  avatarUrl: text('avatar_url'),
  isActive: boolean('is_active').default(true),
  createdAt: timestamp('created_at').defaultNow(),
});

export const posts = pgTable('posts', {
  id: serial('id').primaryKey(),
  title: text('title').notNull(),
  content: text('content'),
  authorId: integer('author_id')
    .notNull()
    .references(() => users.id),
  publishedAt: timestamp('published_at'),
  viewCount: integer('view_count').default(0),
});

// Types are inferred automatically — no generation needed
type User = typeof users.$inferSelect;
type NewUser = typeof users.$inferInsert;

Compare with the equivalent Prisma schema:

// schema.prisma — Prisma DSL
model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String
  avatarUrl String?  @map("avatar_url")
  isActive  Boolean  @default(true) @map("is_active")
  createdAt DateTime @default(now()) @map("created_at")
  posts     Post[]

  @@map("users")
}

model Post {
  id          Int       @id @default(autoincrement())
  title       String
  content     String?
  authorId    Int       @map("author_id")
  author      User      @relation(fields: [authorId], references: [id])
  publishedAt DateTime? @map("published_at")
  viewCount   Int       @default(0) @map("view_count")

  @@map("posts")
}
// Then you must run: npx prisma generate

The Code-First Advantage

Because the schema is TypeScript, you can leverage every language feature: split schemas across multiple files, import/export, conditional logic, shared utilities. You're not limited by a custom DSL's syntax.

4. Query Builder — The SQL You Already Know

Drizzle's query builder maps nearly 1:1 to SQL syntax. If you can write SQL, you can write Drizzle.

import { db } from './db';
import { users, posts } from './schema';
import { eq, gt, desc, sql, and, like } from 'drizzle-orm';

// Basic SELECT
const allUsers = await db.select().from(users);

// WHERE + ORDER BY
const activeUsers = await db
  .select()
  .from(users)
  .where(eq(users.isActive, true))
  .orderBy(desc(users.createdAt));

// JOIN
const usersWithPosts = await db
  .select({
    userName: users.name,
    postTitle: posts.title,
    views: posts.viewCount,
  })
  .from(users)
  .innerJoin(posts, eq(users.id, posts.authorId))
  .where(gt(posts.viewCount, 100));

// INSERT returning
const [newUser] = await db
  .insert(users)
  .values({ email: 'dev@example.com', name: 'Dev' })
  .returning();

// UPDATE
await db
  .update(posts)
  .set({ viewCount: sql`${posts.viewCount} + 1` })
  .where(eq(posts.id, 42));

// Subquery with aggregation
const topAuthors = await db
  .select({
    name: users.name,
    totalViews: sql<number>`sum(${posts.viewCount})`.as('total_views'),
  })
  .from(users)
  .innerJoin(posts, eq(users.id, posts.authorId))
  .groupBy(users.name)
  .having(gt(sql`sum(${posts.viewCount})`, 1000))
  .orderBy(desc(sql`total_views`));

5. Relational Queries — When You Want API-Style Access

Alongside the SQL-style query builder, Drizzle also provides a Relational Query API for those who prefer Prisma-style nested queries:

import { relations } from 'drizzle-orm';

// Define relations
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 with nested relations — Prisma-style
const result = await db.query.users.findMany({
  with: {
    posts: {
      where: gt(posts.viewCount, 50),
      orderBy: desc(posts.publishedAt),
      limit: 5,
    },
  },
  where: eq(users.isActive, true),
});

6. Performance Comparison: Drizzle vs Prisma 7

MetricDrizzle 0.45.xPrisma 7.xPrisma 5.x (legacy)
Bundle size~12KB~1.6MB (600KB gzip)~13MB
Cold start (Lambda)50-100ms80-150ms600-1800ms
Simple query overhead0.5-1ms1-2ms3-5ms
Complex join overhead1-3ms2-5ms5-10ms
Code generationNot requiredRequires prisma generateRequired
Dependencies0MultipleMultiple + Rust binary
Tree-shakeableYesPartialNo

Benchmarks in Real-World Context

The query overhead difference (0.5ms vs 2ms) is rarely the bottleneck. Database round-trip latency is typically 5-50ms — ORM overhead accounts for only 2-10% of total query time. Drizzle truly excels in bundle size and cold start — two critical factors for edge and serverless deployments.

7. Migrations — Two Flexible Modes

Drizzle Kit offers two migration workflows:

7.1 Generate + Migrate (Production)

# Generate SQL migration file from schema changes
npx drizzle-kit generate --name add_user_avatar

# Review the SQL to be executed
cat drizzle/0001_add_user_avatar.sql

# Apply the migration
npx drizzle-kit migrate

7.2 Push (Prototyping)

# Push schema directly to DB, no migration file created
npx drizzle-kit push

# Suitable for rapid development, NOT for production

7.3 Drizzle Studio — Data Management GUI

# Open web UI to browse and edit data
npx drizzle-kit studio

Drizzle Studio runs in the browser, supports schema viewing, data querying, and direct record editing — similar to Prisma Studio but without separate installation.

8. Database Support

DatabaseDriverEdge-compatible
PostgreSQLpostgres, pg, @neondatabase/serverlessNeon, Supabase
MySQLmysql2, @planetscale/databasePlanetScale
SQLitebetter-sqlite3, @libsql/clientTurso, Cloudflare D1
SQL ServerCommunity driverLimited
Gel (new)Dedicated Gel dialectIn development

Optimal Edge Database Stack

Combine Drizzle + Turso (libSQL on edge) or Drizzle + Cloudflare D1 for a fully edge-native database layer — zero latency to database, near-instant cold starts. This is the stack many startups are choosing in 2026.

9. Integration with Vue.js and Nuxt

Drizzle works perfectly in Nuxt server routes and Vue full-stack apps:

// server/utils/db.ts — Nuxt 4 server utility
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import * as schema from '~/server/db/schema';

const client = postgres(process.env.DATABASE_URL!);
export const db = drizzle(client, { schema });

// server/api/posts.get.ts — API route
export default defineEventHandler(async () => {
  return db.query.posts.findMany({
    with: { author: true },
    where: eq(schema.posts.publishedAt, sql`IS NOT NULL`),
    orderBy: desc(schema.posts.publishedAt),
    limit: 20,
  });
});

10. When to Choose Drizzle vs Prisma

graph TD
    A["Choose a TypeScript ORM"] --> B{"Deploying to edge?
Bundle size matters?"} B -->|"Yes"| C["✅ Drizzle"] B -->|"No"| D{"Team comfortable
with SQL?"} D -->|"Yes"| E["✅ Drizzle"] D -->|"No, want higher
abstraction"| F["✅ Prisma"] A --> G{"Need rich ecosystem
(Studio, Accelerate)?"} G -->|"Yes"| H["✅ Prisma"] G -->|"No, need lightweight
and fast"| I["✅ Drizzle"] style A fill:#2c3e50,stroke:#fff,color:#fff style C fill:#e94560,stroke:#fff,color:#fff style E fill:#e94560,stroke:#fff,color:#fff style F fill:#4CAF50,stroke:#fff,color:#fff style H fill:#4CAF50,stroke:#fff,color:#fff style I fill:#e94560,stroke:#fff,color:#fff style B fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50 style D fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50 style G fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
Decision tree: Drizzle vs Prisma depending on project context
✅ Choose Drizzle✅ Choose Prisma
Edge / serverless deploy (small bundle)Team new to SQL, needs abstraction
Team proficient in SQLLarge project needing rich ecosystem
No code generation step desiredPrefer clear schema DSL
Maximum tree-shaking neededNeed Prisma Accelerate (connection pooling)
Multiple database backendsTeam already familiar with Prisma workflow

11. Quick Start — 5 Minutes with Drizzle

# Installation
npm install drizzle-orm postgres
npm install -D drizzle-kit

# Create config
# drizzle.config.ts
import { defineConfig } from 'drizzle-kit';

export default defineConfig({
  schema: './src/schema.ts',
  out: './drizzle',
  dialect: 'postgresql',
  dbCredentials: {
    url: process.env.DATABASE_URL!,
  },
});

# Create schema, push to DB, start coding
npx drizzle-kit push
npx drizzle-kit studio  # Open GUI

12. Conclusion

Drizzle ORM represents a new generation of tooling: lightweight, type-safe, SQL-first, and optimized for the edge computing era. With a bundle size of just 12KB, zero dependencies, and cold starts twice as fast as Prisma, Drizzle is the natural choice for projects deploying to Cloudflare Workers, Vercel Edge, or any serverless environment.

Prisma remains a powerful ORM with a robust ecosystem — especially after Prisma 7 removed the Rust engine. But if you want an ORM that "thinks with you" rather than "thinks for you," Drizzle deserves a try.

Where to Start?

If using Nuxt/Vue: check out the Drizzle Quick Start + set up server/utils/db.ts. For plain Node.js: npm install drizzle-orm postgres and start defining your schema. The entire process from install to first query takes about 5 minutes.

References: