Daniel Howells

Drizzle Over Prisma

2026-01-02

I used Prisma for a long time. It was fine. The schema language was readable, the migrations worked, the client was decent. But "fine" accumulates friction, and eventually I switched to Drizzle and won't go back.

The schema is TypeScript. Not a .prisma file that compiles to TypeScript — actual TypeScript that IS the schema. I define tables with pgTable, columns with typed helpers, indexes with .on(). The types are inferred directly from the definitions. No code generation step, no prisma generate, no waiting for the client to rebuild after every schema change.

Push-only migrations changed my development workflow. In development, I run db:push and Drizzle diffs the schema against the database and applies changes directly. No migration files to manage, no migration history to keep in sync, no prisma migrate reset when things get tangled. The schema file is the source of truth. In production you can generate SQL migrations if you need them, but in development the push workflow is dramatically faster.

The query builder is closer to SQL than Prisma's. This matters when your queries get complex. Materia has products with 165 attributes in JSONB columns, vector embeddings with HNSW indexes, hierarchical categories using ltree, quantised colour data across dual colour models. Writing these queries in Drizzle feels like writing SQL with type safety. In Prisma, complex queries meant escaping to $queryRaw and losing all the type benefits.

Custom column helpers keep the schema DRY. I have id() that generates nanoid primary keys, vector() for pgvector columns with configurable dimensions, timestamp helpers with timezone handling. These compose naturally because they're just functions returning column definitions.

The lazy database client was a small thing that made a big difference. Drizzle wraps the connection in a Proxy that defers initialisation until first use. No eager connection on import, no startup penalty in serverless environments, no connection management boilerplate.

The sql template literal is powerful. JSONB extraction, custom operators, window functions — anything Postgres can do, the template literal can express, and the result type flows through to the consumer. sql<VariantAttributes> tells the type system exactly what shape the raw query returns.

I don't think Prisma is bad. For simpler schemas and teams that prefer a more abstracted API, it works well. But once your data model gets complex enough that you're fighting the ORM instead of using it, Drizzle's closer-to-the-metal approach wins.