TimoBlog
Back to all posts

🚀 Why I Switched to Drizzle ORM (And Why You Might Too)

Timothy Benjamin Timothy Benjamin
5 min read
🚀 Why I Switched to Drizzle ORM (And Why You Might Too)

Table of Contents

Share this post

🚀 Why I Switched to Drizzle ORM (And Why You Might Too)

As a fullstack dev juggling PostgreSQL and TypeScript, I’ve worked with Prisma, TypeORM, Sequelize—you name it. But then came Drizzle ORM, and it just clicked.

The ORM Journey

Let me take you through my database journey. Like many developers, I started with raw SQL queries (ah, those were the days), then moved to query builders, and eventually settled into the comfortable abstraction of ORMs.

Each step of the way brought benefits but also compromises. Traditional ORMs often felt like they were fighting against SQL rather than working with it. They’d abstract away the database to make it “easier,” but in doing so, they’d hide the very power I needed for complex queries.

Then Drizzle entered the scene.

What Made Me Switch?

There are four key reasons Drizzle has completely changed my database workflow:

âś… Fully typed, no codegen

This was the game-changer for me. With Prisma, every schema change meant running a codegen step and waiting. With TypeORM, the types often felt bolted on rather than integral.

Drizzle gives me fully typed queries without any code generation step. I define my schema in TypeScript, and the types just work:

import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';

// Define the schema
export const users = pgTable('users', {
  id: serial('id').primaryKey(),
  email: text('email').notNull().unique(),
  name: text('name'),
  createdAt: timestamp('created_at').defaultNow()
});

// Types are automatically inferred
const newUser = await db.insert(users).values({
  email: 'me@example.com', 
  name: 'My Name'
}).returning();

// TypeScript knows exactly what this is
console.log(newUser[0].email);

No more waiting for code generation. No more type mismatches. Just write and run.

âś… Insanely fast DX

Developer experience matters enormously when you’re working with a tool all day. With Drizzle, everything just feels fast.

Schema changes are immediate—no waiting for migrations to be generated (though Drizzle has great migration tools when you need them). Queries are predictable and align closely with the SQL they generate, making debugging straightforward.

My development loop went from:

  1. Change schema → 2. Run codegen → 3. Fix type errors → 4. Test → 5. Repeat

To:

  1. Change schema → 2. Test → 3. Repeat

That middle step was killing my flow state, and now it’s gone.

âś… Works with SQL, not against it

If you know SQL, you’ll feel right at home with Drizzle. The query builder follows SQL-like patterns without trying to reinvent database concepts:

const result = await db
  .select({
    userId: users.id, 
    name: users.name,
    postCount: sql`count(${posts.id})`.as('post_count')
  })
  .from(users)
  .leftJoin(posts, eq(posts.userId, users.id))
  .where(eq(users.isActive, true))
  .groupBy(users.id, users.name)
  .having(gt(sql`count(${posts.id})`, 5))
  .orderBy(desc(users.createdAt))
  .limit(10);

This query looks almost exactly like the SQL it generates, which means:

  1. I can reason about performance directly
  2. I can easily translate SQL examples to Drizzle code
  3. When I need to drop to raw SQL for something complex, the transition is seamless

âś… Built for the modern TS/Postgres stack

Drizzle feels like it was made specifically for my stack:

  • TypeScript without compromise
  • PostgreSQL (including advanced features like JSON and arrays)
  • Modern Node.js with ESM
  • Works beautifully with frameworks like Next.js and Remix

It’s not trying to support every database under the sun with lowest-common-denominator features. Instead, it embraces what makes PostgreSQL special while still supporting other databases like MySQL and SQLite.

Real-World Example: My Forms AI App

I’m now using Drizzle in production projects, including a Forms AI app with PostgreSQL, pgvector for embeddings, and GraphQL.

Here’s a simplified version of my schema:

import { pgTable, uuid, text, jsonb, timestamp, boolean, vector } from 'drizzle-orm/pg-core';

export const forms = pgTable('forms', {
  id: uuid('id').defaultRandom().primaryKey(),
  title: text('title').notNull(),
  fields: jsonb('fields').$type<FormField[]>().notNull(),
  published: boolean('published').default(false),
  // Vector embeddings for semantic search
  contentEmbedding: vector('content_embedding', { dimensions: 1536 }),
  createdAt: timestamp('created_at').defaultNow()
});

export const submissions = pgTable('submissions', {
  id: uuid('id').defaultRandom().primaryKey(),
  formId: uuid('form_id').references(() => forms.id),
  data: jsonb('data').notNull(),
  submittedAt: timestamp('submitted_at').defaultNow()
});

When implementing semantic search with pgvector, Drizzle made it incredibly easy:

const similarForms = await db
  .select({
    id: forms.id,
    title: forms.title,
    // Calculate cosine similarity
    similarity: sql`1 - (${forms.contentEmbedding} <=> ${queryEmbedding})`.as('similarity')
  })
  .from(forms)
  .where(eq(forms.published, true))
  .orderBy(desc(sql`similarity`))
  .limit(5);

The ability to seamlessly mix SQL functions with typed queries is exactly what I needed for this project.

The Little Things That Make a Difference

Beyond the big features, there are countless small touches that show Drizzle was built by developers who actually use databases daily:

  • Transactions are first-class citizens with a clean API
  • Relations feel natural instead of being an afterthought
  • Partial selects let me build efficient queries that only return what I need
  • Debugging is straightforward because the generated SQL is predictable
  • Runtime is tiny compared to other ORMs, which matters for serverless functions
  • TypeScript inference is rock-solid, catching subtle errors early

Is Drizzle Perfect?

Of course not. No technology is. The documentation, while good, is still maturing. Some advanced PostgreSQL features require dropping to raw SQL (though this is improving rapidly). And if you’re looking for an Active Record pattern, Drizzle doesn’t follow that approach.

But for a type-safe, SQL-focused ORM that feels like it was designed for modern TypeScript development, Drizzle has become my clear first choice.

Should You Switch?

If you’re happy with your current ORM, there’s no need to jump ship immediately. But if you’re experiencing any of these pain points:

  • Slow development cycles due to code generation
  • Fighting with your ORM to write the SQL you want
  • Type safety issues or runtime errors from ORM abstractions
  • Performance problems from inefficient query generation

…then Drizzle might be exactly what you’ve been looking for.

Timothy Benjamin

About Timothy Benjamin

A Freelance Full-Stack Developer who brings company website visions to reality.