This page compares Prisma ORM and Drizzle.

Drizzle vs Prisma ORM

While Prisma ORM and Drizzle solve similar problems, they work in very different ways and have individual pros and cons. Which one to choose will depend on the needs of your project and the exact tradeoffs that are important for it.

Drizzle is a traditional SQL query builder that lets you compose SQL queries with JavaScript/TypeScript functions. It can be used to query a database or run migrations. Drizzle also offers a Queries API, which offers a higher level abstraction from SQL and can be used to read nested relations. Drizzle schema is defined in TypeScript files, which are used to generate SQL migrations and are then executed against a database.

Prisma ORM mitigates many problems of traditional ORMs, such as bloated model instances, mixing business with storage logic, lack of type-safety or unpredictable queries caused e.g. by lazy loading. It uses the Prisma schema to define application models in a declarative way. Prisma Migrate then allows the generation of SQL migrations from the Prisma schema and executes them against the database. CRUD queries are provided by Prisma Client, a lightweight and entirely type-safe database client for Node.js and TypeScript.

API design & Level of abstraction

Drizzle and Prisma ORM operate on different levels of abstraction. Drizzle's philosophy is "If you know SQL, you know Drizzle ORM". It mirrors SQL in its API while Prisma Client provides a higher-level abstraction that was designed with the common tasks of application developers in mind. Prisma ORM's API design heavily leans on the idea of .

While Prisma Client operates on a higher level of abstraction, you are able to drop down to raw SQL at any time. However, full use of Prisma ORM and development of your application does not require SQL knowledge. Prisma ORM's goal is to construct a query syntax focused on developer experience and productivity that feels familiar to developers. You can learn more about this here: Why Prisma.

The following sections examine a few examples of how Prisma ORM's and Drizzle's APIs differ in certain scenarios and what the rationale of Prisma ORM's API design is in these cases.

Data modeling

Prisma models are defined in the Prisma schema, while Drizzle uses TypeScript functions for table definitions. These functions are then exported and used in queries.

Prisma generates a lightweight database client that exposes a tailored and fully type-safe API to read and write data for the models that are defined in the Prisma schema, following the DataMapper ORM pattern.

Prisma ORM's DSL for data modeling is lean, simple and intuitive to use. When modeling data in VS Code, you can further take advantage of Prisma ORM's powerful with features like autocompletion, quick fixes, jump to definition and other benefits that increase developer productivity. On the other hand, Drizzle's use of TypeScript means that you can lean on the power of TypeScript for additional flexibility (via reused code, for example).

Prisma ORM
model User {
id Int @id @default(autoincrement())
name String?
email String @unique
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
authorId Int?
author User? @relation(fields: [authorId], references: [id])
}
Drizzle
import {
boolean,
integer,
pgTable,
serial,
text,
uniqueIndex,
varchar,
} from 'drizzle-orm/pg-core'
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: varchar('name', { length: 256 }),
email: varchar('email', { length: 256 }).unique(),
})
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: varchar('title', { length: 256 }).notNull(),
content: text('content'),
published: boolean('published'),
authorId: integer('author_id').references(() => users.id),
})

Migrations

Migrations work similarly between Drizzle and Prisma ORM. Both tools follow the approach of generating SQL files based on the provided model definitions and provide a CLI to execute them against the database. The SQL files can be modified before the migrations are executed so that any custom database operation can be performed with either migration system.

Querying

Plain queries are natural to construct in both Drizzle and Prisma ORM. Using Drizzle's Queries API, the two approaches are very similar:

Prisma ORM
// find all users
const allUsers = await prisma.user.findMany()
// find a single user
const user = await prisma.user.findFirst({
where: { id: 27 },
})
// find a unique user
const user = await prisma.user.findUnique({
where: { email: 'nilu@prisma.io' },
})
Drizzle
import { eq } from 'drizzle-orm'
// find all users
const allUsers = await db.query.users.findMany()
// find a single user
const user = await db.query.users.findFirst({
where: eq(users.id, 1),
})
// find a unique post
const user = await db.query.users.findFirst({
where: eq(users.email, 'nilu@prisma.io'),
})

When performing a mutation, a create, update, or delete, the Drizzle Queries API is not available. In these cases, you will need to use Drizzle's SQL-like APIs:

Prisma ORM
// create a user
const user = await prisma.user.create({
data: {
name: 'Nilu',
email: 'nilu@prisma.io',
},
})
// update a user
const user = await prisma.user.update({
where: { email: 'nilu@prisma.io' },
data: { name: 'Another Nilu' },
})
// delete a user
const deletedUser = await prisma.user.delete({
where: { email: 'nilu@prisma.io' },
})
Drizzle
// create a user
const user = await db.insert(users).values({
name: 'Nilu',
email: 'nilu@prisma.io',
})
// update a user
const user = await db
.update(users)
.set({ name: 'Another Nilu' })
.where(eq(users.email, 'nilu@prisma.io'))
.returning()
// delete a user
const deletedUser = await db
.delete(users)
.where(eq(users.email, 'nilu@prisma.io'))
.returning()

Relations

Working with records that are connected via foreign keys can become very complex in SQL. Prisma ORM's concept of virtual relation field enables an intuitive and convenient way for application developers to work with related data. Some benefits of Prisma ORM's approach are:

  • traversing relationships via the fluent API (docs)
  • nested writes that enable updating/creating connected records (docs)
  • applying filters on related records (docs)
  • easy and type-safe querying of nested data without worrying about underlying SQL (docs)
  • creating nested TypeScript typings based on models and their relations (docs)
  • intuitive modeling of relations in the data model via relation fields (docs)
  • implicit handling of relation tables (also sometimes called JOIN, link, pivot or junction tables) (docs)
Prisma ORM
const posts = await prisma.post.findMany({
include: {
author: true,
},
})
Drizzle
const posts = await db.query.posts.findMany({
with: {
author: true,
},
})

Filtering

Drizzle exposes the underlying filter and conditional operators for a given SQL dialect. Prisma ORM on the other hand, provides a more generic set of operators that are intuitive to use.

A good example of how the filtering APIs of both Drizzle and Prisma ORM differ is by looking at string filters. While Drizzle provides filters for like and ilike, Prisma ORM provides more specific operators that developers can use, e.g.: contains, startsWith and endsWith.

Prisma ORM
// case sensitive filter
const posts = await prisma.post.findMany({
where: {
title: 'Hello World',
},
})
// case insensitive filter
const posts = await prisma.post.findMany({
where: {
title: 'Hello World',
mode: 'insensitive',
},
})
Drizzle
// case sensitive filter
const posts = await db
.select()
.from(posts)
.where(like(posts.title, 'Hello World'))
// case insensitive filter
const posts = await db
.select()
.from(posts)
.where(ilike(posts.title, 'Hello World'))
Prisma ORM
const posts = await prisma.post.findMany({
where: {
title: {
contains: 'Hello World',
},
},
})
Drizzle
const posts = await db
.select()
.from(posts)
.where(ilike(posts.title, '%Hello World%'))
Prisma ORM
const posts = await prisma.post.findMany({
where: {
title: {
startsWith: 'Hello World',
},
},
})
Drizzle
const posts = await db
.select()
.from(posts)
.where(ilike(posts.title, 'Hello World%'))
Prisma ORM
const posts = await prisma.post.findMany({
where: {
title: {
endsWith: 'Hello World',
},
},
})
Drizzle
const posts = await db
.select()
.from(posts)
.where(ilike(posts.title, '%Hello World'))

Pagination

Drizzle only offers limit-offset pagination while Prisma ORM conveniently provides dedicated APIs for both limit-offset but also cursor-based. You can learn more about both approaches in the Pagination section of the docs.

Prisma ORM
// limit-offset pagination
const postPage = await prisma.post.findMany({
where: {
title: 'Hello World',
},
skip: 6,
take: 3,
})
// cursor-based pagination
const postPage = await prisma.post.findMany({
where: {
title: 'Hello World',
},
cursor: { id: 7 },
take: 3,
})
Drizzle
// limit-offset pagination (cursor-based not currently possible)
const postPage = await db
.select()
.from(users)
.where(ilike(posts.title, 'Hello World%'))
.limit(3)
.offset(6)

Observability

Both Drizzle and Prisma ORM have the ability to log queries and the underlying SQL generated.

Prisma ORM has additional features built into the client that help teams get a better understanding of their data usage. Metrics and tracing are two features that can be enabled at any time and give you per query information. This information can be integrated with external tools so that you can track performance over time.

Additional products

Both Drizzle and Prisma offer products alongside an ORM. Prisma Studio was released to allow users to interact with their database via a GUI and also allows for limited self-hosting for use within a team. Drizzle Studio was released to accomplish the same tasks.

In addition to Prisma Studio, Prisma offers commercial products via the Prisma Data Platform:

  • : A connection pooler and global cache that integrates with Prisma ORM. Users can take advantage of connection pooling immediately and can control caching at an individual query level.
  • : A change data capture (CDC) service where Prisma Client can subscribe to database changes and receive them in real-time with little to no setup.

These products work hand-in-hand with Prisma ORM to offer comprehensive data tooling, making building data-driven applications easy by following principles.

Ecosystem

Both Drizzle and Prisma ORM have cases where users want to do something not directly supported by the library. Drizzle relies on the expressiveness of SQL to avoid these cases, while Prisma ORM has Prisma Client extensions to allow any user to add additional behaviors to their instance of Prisma Client. These extensions are also shareable, meaning teams can develop them for use across their projects or even for use by other teams.

While Drizzle is a relatively new product, Prisma ORM was and is well established in the JavaScript/TypeScript space. It has proven value , many companies trust .

Prisma ORM is also included as the data layer tool of choice in many meta-frameworks and development platforms like , , , , and the .

Thanks to its maturity, Prisma's community has developed a that helps with various Prisma workflows. Here are a few highlights:

  • : Toolkit that extends Prisma with access control policies in the Prisma schema, auto-generated CRUD APIs and frontend query hooks.
  • : Visualizes the Prisma schema as an entity-relationship-diagram (ERD).
  • : Generates schemas from the Prisma schema.
  • : Let's you access your database from the frontend using Prisma Client.
  • : Environment for Prisma integrated testing with .
  • : Creates GraphQL types based on Prisma models when using .
  • : Creates routers from your Prisma schema.

Database support

Both Drizzle and Prisma ORM support multiple and different kinds of databases. Drizzle achieves this support through driver implementations created by Drizzle, which integrate with existing third-party database drivers.

Prisma ORM has begun adding support for , but primarily uses built-in drivers to communicate with an underlying database. Prisma also defaults connections to TLS, which improves security.

Additionally, Prisma ORM supports CockroachDB, Microsoft SQL Server, and MongoDB, which Drizzle does not currently support. Prisma ORM also offers the relation mode that allows Prisma ORM to emulate foreign key constraints for those database engines that do not support it. Drizzle currently supports Cloudflare D1, bun:sqlite, and SQLite via HTTP Proxy, which Prisma ORM currently does not.

Conclusion

Both Drizzle ORM and Prisma ORM are tools for data access and migrations. Drizzle is focused on being a thin wrapper around a SQL-like syntax while Prisma is focused on a convenient and expressive API. Other important differences include Prisma ORM's support of MSSQL and MongoDB, support for additional features via Prisma Client extensions, additional cloud-ready products, and a robust ecosystem.

For teams that use SQL daily, Drizzle offers a convenient wrapper that will feel familiar and is type-safe. Prisma ORM's $queryRaw feature, while available, does not provide the same level of type safety. However, the experience of writing raw SQL in Prisma ORM can be enhanced using the community plugin for Prisma ORM, offering syntax highlighting and type-checking in your editor as well.

On the other hand, for teams that are a mix of developers (front-end, back-end, and full-stack) that have varying levels of experience with databases, Prisma ORM offers a comprehensive and easy-to-learn approach for data access and managing database schemas.