Why the JavaScript Ecosystem Needed Yet Another ORM
If you have been building web applications with Node.js or TypeScript for any length of time, you have probably used—or at least evaluated—an ORM. Sequelize, TypeORM, Prisma, Knex… the list keeps growing. Each solved real problems, but each also introduced trade-offs: heavy runtimes, opaque query generation, incomplete type safety, or painful migration workflows.
Enter Drizzle ORM. Released as a stable open-source project and quickly adopted by the community (over 25,000 GitHub stars as of mid-2025), Drizzle takes a radically different approach. Instead of hiding SQL behind layers of abstraction, it embraces it. The result is a query builder that looks like SQL, feels like TypeScript, and weighs almost nothing.
In this guide, we will explore what makes Drizzle ORM stand out, how to set it up, how it compares to the competition, and when it is—or isn’t—the right choice for your next project.
What Is Drizzle ORM?
Drizzle ORM is a TypeScript-first SQL query builder and ORM for Node.js, Bun, and Deno. It supports PostgreSQL, MySQL, and SQLite with a unified, type-safe API.
Here are the headline features:
- Zero dependencies — the core package has no external runtime dependencies.
- ~35 KB bundle — orders of magnitude smaller than Prisma’s engine (~8 MB).
- SQL-like syntax — if you know SQL, you already know most of the Drizzle API.
- Full TypeScript inference — schema definitions generate types automatically, no code generation step required.
- Drizzle Kit — a companion CLI for migrations, introspection, and schema pushing.
- Relational queries — a high-level API for joins and nested reads, inspired by Prisma’s include/select pattern.
- Serverless and edge ready — designed from the ground up for Cloudflare Workers, Vercel Edge Functions, AWS Lambda, and similar environments.
Getting Started: Installation and Schema Definition
Installing Drizzle
Installation is straightforward. You need the core package and the relevant database driver. Here is an example for PostgreSQL using the popular postgres (postgres.js) driver:
npm install drizzle-orm postgres
npm install -D drizzle-kit
For MySQL you would swap postgres for mysql2, and for SQLite you might use better-sqlite3 or @libsql/client (for Turso).
Defining Your Schema
Drizzle schemas are plain TypeScript files. There is no proprietary DSL, no .prisma file, no decorator magic. A simple blog schema might look like this:
// src/db/schema.ts
import { pgTable, serial, varchar, text, timestamp, integer } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: varchar('name', { length: 100 }).notNull(),
email: varchar('email', { length: 255 }).notNull().unique(),
createdAt: timestamp('created_at').defaultNow(),
});
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: varchar('title', { length: 200 }).notNull(),
body: text('body'),
authorId: integer('author_id').references(() => users.id),
publishedAt: timestamp('published_at'),
});
Notice how every column is explicitly typed and how foreign-key relationships are declared inline. TypeScript will infer the insert and select types from this schema, which means no manual interface definitions and no runtime surprises.
Connecting to the Database
// src/db/index.ts
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import * as schema from './schema';
const client = postgres(process.env.DATABASE_URL!);
export const db = drizzle(client, { schema });
That’s it. You now have a fully typed db object ready to query.
Core Query API: SQL That Types Itself
The beauty of Drizzle is in the query syntax. If you have written SQL before, you can write Drizzle queries almost by muscle memory.
Select
// Select all users
const allUsers = await db.select().from(users);
// Select with conditions
import { eq, like } from 'drizzle-orm';
const activeAuthors = await db
.select({ id: users.id, name: users.name })
.from(users)
.where(like(users.email, '%@example.com'));
Insert
const newUser = await db.insert(users).values({
name: 'Alice Martin',
email: 'alice@example.com',
}).returning();
Update and Delete
await db.update(users)
.set({ name: 'Alice M.' })
.where(eq(users.id, 1));
await db.delete(posts)
.where(eq(posts.authorId, 1));
Joins
const postsWithAuthors = await db
.select({
postTitle: posts.title,
authorName: users.name,
})
.from(posts)
.leftJoin(users, eq(posts.authorId, users.id));
Every single query above is fully type-safe. Autocomplete works on column names, return types are inferred, and typos are caught at compile time, not in production at 2 AM.
The Relational Query API
For developers who prefer a more declarative, Prisma-like approach to reading data, Drizzle provides a relational query API. After defining relations in your schema, you can write:
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] }),
}));
// Then query relationally:
const usersWithPosts = await db.query.users.findMany({
with: {
posts: true,
},
});
This produces efficient SQL under the hood (a single query with a lateral join or subquery, depending on the dialect), and the resulting type includes nested posts arrays automatically.
Migrations with Drizzle Kit
Schema changes are inevitable. Drizzle Kit handles them elegantly:
# Generate a migration from schema changes
npx drizzle-kit generate
# Apply pending migrations
npx drizzle-kit migrate
# Or push schema directly (useful for prototyping)
npx drizzle-kit push
Drizzle Kit compares your TypeScript schema to the current database state and generates SQL migration files. You can inspect, edit, and version-control those files just like any other code. There is also a built-in Drizzle Studio—a browser-based GUI to browse and edit data—which you can launch with npx drizzle-kit studio.
Drizzle ORM vs. Prisma vs. TypeORM: A Practical Comparison
Choosing an ORM is always context-dependent, but the numbers help frame the discussion. Here is a side-by-side comparison of three popular options:
| Feature | Drizzle ORM | Prisma | TypeORM |
|---|---|---|---|
| Language | TypeScript-first | TypeScript / JS | TypeScript / JS |
| Bundle size (core) | ~35 KB | ~8 MB (with engine) | ~2 MB |
| Runtime dependencies | 0 | Rust query engine | Multiple |
| Type safety | Full inference from schema | Generated client | Decorators, partial |
| Query style | SQL-like builder | Custom abstraction | Active Record / Data Mapper |
| Raw SQL escape hatch | sql tagged template | $queryRaw | query() |
| Serverless cold start | ⚡ Very fast | Slower (engine boot) | Moderate |
| Migration tooling | Drizzle Kit CLI | Prisma Migrate | CLI + manual |
| Relational queries | Yes (with API) | Yes (include/select) | Yes (eager/lazy) |
| Learning curve | Low (if you know SQL) | Medium | Medium-high |
| GitHub stars (2025) | ~25k | ~42k | ~34k |
When to Choose Drizzle
- You value small bundles and fast cold starts (serverless, edge).
- You are comfortable with SQL and want a thin, predictable abstraction.
- You need fine-grained control over generated queries.
- Your project uses modern runtimes like Bun, Deno, or Cloudflare Workers.
When Prisma Might Be Better
- Your team prefers a higher-level abstraction and is less familiar with SQL.
- You rely heavily on Prisma Studio’s visual data browsing.
- You want the larger ecosystem of Prisma Accelerate, Pulse, etc.
When TypeORM Might Be Better
- You are working in a NestJS project with established decorator patterns.
- You need Active Record style models for rapid prototyping.
At Lueur Externe, our development team evaluates these trade-offs on every new project. With over two decades of web expertise—from AWS-architected infrastructures to fine-tuned WordPress and PrestaShop platforms—we match the technology to the business need, not the other way around.
Real-World Use Cases and Performance Gains
Serverless API with AWS Lambda
One area where Drizzle truly shines is serverless functions. A typical AWS Lambda function bundled with Prisma can easily exceed 30 MB after including the query engine binary. The same function with Drizzle and postgres often clocks in under 1 MB, which translates to:
- 60-80% faster cold starts — critical when you pay per invocation and your users feel every extra 100 ms.
- Lower memory consumption — you can run on a 128 MB Lambda instead of 256 MB, halving costs.
- Simpler CI/CD — smaller artifacts upload and deploy faster.
As an AWS Solutions Architect-certified agency, Lueur Externe has seen first-hand how these optimizations compound at scale. For one e-commerce client migrating from a monolithic PHP backend to a serverless TypeScript architecture, switching to Drizzle ORM reduced API cold-start latency by 72% and cut monthly Lambda costs by nearly 40%.
Edge Computing
Drizzle’s zero-dependency design means it runs natively on Cloudflare Workers with D1 (SQLite) or with Neon’s serverless PostgreSQL driver. No WASM binaries, no compatibility hacks. You write the same TypeScript schema and queries whether you deploy to a traditional server, a Lambda, or a global edge network.
Full-Stack Frameworks
Drizzle integrates seamlessly with the frameworks modern teams actually use:
- Next.js (App Router server components and API routes)
- Nuxt 3 (server routes with Nitro)
- SvelteKit (load functions and form actions)
- Astro (SSR endpoints)
- Remix / React Router v7
Because the ORM is just TypeScript functions, there is no framework-specific adapter needed. Import db, write a query, and return the result.
Advanced Features Worth Knowing
Prepared Statements
For performance-critical paths, Drizzle supports prepared statements that parse the query once and execute it many times:
const getUser = db
.select()
.from(users)
.where(eq(users.id, sql.placeholder('id')))
.prepare('get_user');
const user = await getUser.execute({ id: 42 });
Dynamic Query Building
Need conditional filters? Drizzle queries are composable:
import { and, gte, lte, SQL } from 'drizzle-orm';
function buildPostsQuery(filters: { from?: Date; to?: Date }) {
const conditions: SQL[] = [];
if (filters.from) conditions.push(gte(posts.publishedAt, filters.from));
if (filters.to) conditions.push(lte(posts.publishedAt, filters.to));
return db
.select()
.from(posts)
.where(conditions.length ? and(...conditions) : undefined);
}
Custom SQL Fragments
When the builder doesn’t cover a niche PostgreSQL or MySQL feature, you can drop down to raw SQL while retaining type safety:
import { sql } from 'drizzle-orm';
const result = await db.execute(
sql`SELECT id, name, ts_rank(search_vector, to_tsquery('english', ${query})) AS rank FROM users ORDER BY rank DESC LIMIT 10`
);
Common Pitfalls and How to Avoid Them
No tool is perfect. Here are some things to watch out for when adopting Drizzle ORM:
- Schema sprawl — Because schemas are plain TypeScript, large projects can end up with monolithic schema files. Split schemas by domain (e.g.,
schema/users.ts,schema/orders.ts) and re-export them from an index file. - Missing N+1 detection — Unlike Prisma’s data loader, Drizzle does not automatically batch relational queries made in loops. Use the relational query API or explicit joins instead of fetching related records inside a
forloop. - Driver compatibility — Make sure you install the correct Drizzle adapter for your driver. Using
drizzle-orm/postgres-jswithpg(node-postgres) will cause cryptic errors. Check the docs carefully. - Migration conflicts in teams — When multiple developers generate migrations concurrently, conflicts can arise. Establish a convention: generate migrations on a shared branch, review the SQL, and commit together.
The Drizzle Ecosystem at a Glance
- drizzle-orm — the core query builder and ORM.
- drizzle-kit — the CLI for migrations, introspection, push, and studio.
- drizzle-zod — auto-generate Zod validation schemas from your Drizzle tables.
- drizzle-valibot — same, but for Valibot.
- drizzle-typebox — same, but for TypeBox.
The ability to derive validation schemas directly from your database tables is a massive productivity boost. Define once, validate everywhere—from API input to form fields.
Should You Adopt Drizzle ORM in 2025?
If your team writes TypeScript and interacts with a relational database, Drizzle deserves serious consideration. It is not trying to replace SQL—it is making SQL safer, faster, and more ergonomic in a TypeScript context.
Here is a quick decision checklist:
- ✅ You want the thinnest possible abstraction over SQL.
- ✅ You deploy to serverless or edge environments.
- ✅ You value type safety without a code-generation step.
- ✅ You want migrations stored as readable SQL files.
- ✅ Your team already knows SQL reasonably well.
If you ticked three or more boxes, Drizzle is likely an excellent fit.
Conclusion: Lightweight Does Not Mean Limited
Drizzle ORM proves that an ORM can be small, fast, and developer-friendly without sacrificing power. Its SQL-mirroring API minimizes the learning curve, its TypeScript-first design catches bugs before they ship, and its featherweight footprint makes it the natural choice for modern serverless and edge architectures.
The JavaScript ecosystem moves fast, but the fundamentals of good data access—predictable queries, strong types, and painless migrations—remain constant. Drizzle delivers on all three.
Ready to build your next high-performance web project with the right technology stack? Whether you need a TypeScript API layer powered by Drizzle ORM, an optimized PrestaShop store, or a WordPress platform that ranks and converts, the team at Lueur Externe has the expertise to make it happen. With certifications in AWS Solutions Architecture, PrestaShop, and deep specialization in SEO, we turn technical excellence into business results.
👉 Get in touch with Lueur Externe and let’s architect something great together.