Documentation Index Fetch the complete documentation index at: https://mintlify.com/drizzle-team/drizzle-orm/llms.txt
Use this file to discover all available pages before exploring further.
Migrating from Prisma to Drizzle ORM is straightforward and can be done incrementally. This guide covers the migration process, API differences, and how to preserve your existing data.
Why Migrate to Drizzle?
Developers choose Drizzle over Prisma for several reasons:
Type Safety Drizzle provides true TypeScript type inference without code generation
SQL-like API Write queries that look like SQL but with full type safety
Zero Dependencies Minimal bundle size with no runtime dependencies
Edge Compatible Works in Cloudflare Workers, Vercel Edge, and other edge runtimes
Performance No query engine overhead, direct database driver access
SQL Control Full control over generated SQL with raw query support
Migration Strategies
Strategy 1: Side-by-Side (Recommended)
Run Prisma and Drizzle together during migration:
Install Drizzle
npm install drizzle-orm
npm install -D drizzle-kit
Generate Drizzle schema from Prisma
Use Drizzle Kit to introspect your existing database: npx drizzle-kit introspect
This creates a Drizzle schema matching your current database structure.
Migrate routes incrementally
Convert one route/feature at a time from Prisma to Drizzle: // Before: Prisma
const users = await prisma . user . findMany ();
// After: Drizzle
const users = await db . select (). from ( usersTable );
Remove Prisma when complete
Once all code is migrated, uninstall Prisma: npm uninstall prisma @prisma/client
Strategy 2: Fresh Start
Start a new project with Drizzle and migrate data:
Create new Drizzle schema from scratch
Export data from Prisma database
Import data into new Drizzle-managed database
Update application code
Schema Conversion
Prisma Schema to Drizzle
Here’s how Prisma schema elements map to Drizzle:
Basic Model
Prisma Schema
Drizzle Schema (PostgreSQL)
Drizzle Schema (MySQL)
model User {
id Int @id @default ( autoincrement ())
email String @unique
name String ?
createdAt DateTime @default ( now ())
updatedAt DateTime @updatedAt
}
Enums
Prisma Schema
Drizzle Schema (PostgreSQL)
Drizzle Schema (MySQL)
enum Role {
USER
ADMIN
}
model User {
id Int @id @default ( autoincrement ())
role Role @default ( USER )
}
Relations
Prisma Schema
Drizzle Schema
model User {
id Int @id @default ( autoincrement ())
posts Post []
}
model Post {
id Int @id @default ( autoincrement ())
title String
userId Int
user User @relation ( fields : [ userId ], references : [ id ] )
}
Many-to-Many Relations
Prisma Schema
Drizzle Schema
model Post {
id Int @id @default ( autoincrement ())
categories Category []
}
model Category {
id Int @id @default ( autoincrement ())
posts Post []
}
Query API Comparison
Finding Records
// Find all
const users = await prisma . user . findMany ();
// Find with where clause
const activeUsers = await prisma . user . findMany ({
where: { active: true },
});
// Find one
const user = await prisma . user . findUnique ({
where: { id: 1 },
});
// Find first
const firstUser = await prisma . user . findFirst ({
where: { active: true },
});
Creating Records
const user = await prisma . user . create ({
data: {
email: 'john@example.com' ,
name: 'John' ,
},
});
// Create many
await prisma . user . createMany ({
data: [
{ email: 'john@example.com' , name: 'John' },
{ email: 'jane@example.com' , name: 'Jane' },
],
});
Updating Records
const user = await prisma . user . update ({
where: { id: 1 },
data: { name: 'John Updated' },
});
// Update many
await prisma . user . updateMany ({
where: { active: false },
data: { deleted: true },
});
Deleting Records
await prisma . user . delete ({
where: { id: 1 },
});
await prisma . user . deleteMany ({
where: { active: false },
});
Filtering
// Equals
const users = await prisma . user . findMany ({
where: { role: 'ADMIN' },
});
// Not equals
const users = await prisma . user . findMany ({
where: { role: { not: 'ADMIN' } },
});
// In array
const users = await prisma . user . findMany ({
where: { role: { in: [ 'ADMIN' , 'MODERATOR' ] } },
});
// Greater than
const users = await prisma . user . findMany ({
where: { age: { gt: 18 } },
});
// Contains (string)
const users = await prisma . user . findMany ({
where: { name: { contains: 'John' } },
});
Combining Filters
// AND
const users = await prisma . user . findMany ({
where: {
AND: [
{ active: true },
{ role: 'ADMIN' },
],
},
});
// OR
const users = await prisma . user . findMany ({
where: {
OR: [
{ role: 'ADMIN' },
{ role: 'MODERATOR' },
],
},
});
Selecting Specific Fields
const users = await prisma . user . findMany ({
select: {
id: true ,
name: true ,
email: true ,
},
});
Relations and Joins
Prisma
Drizzle (Relational API)
Drizzle (Manual Joins)
const users = await prisma . user . findMany ({
include: {
posts: true ,
},
});
// Nested includes
const users = await prisma . user . findMany ({
include: {
posts: {
include: {
comments: true ,
},
},
},
});
Sorting and Pagination
const users = await prisma . user . findMany ({
orderBy: { createdAt: 'desc' },
skip: 10 ,
take: 20 ,
});
Aggregations
const result = await prisma . user . aggregate ({
_count: true ,
_avg: { age: true },
_sum: { age: true },
});
Transactions
await prisma . $transaction ( async ( tx ) => {
await tx . user . create ({ data: { name: 'John' } });
await tx . post . create ({ data: { title: 'Post' } });
});
Data Migration
Preserving Existing Data
Your data stays in the database during migration. Only your application code changes.
Introspect existing database
npx drizzle-kit introspect
This generates a Drizzle schema matching your current Prisma database structure.
Review generated schema
Check drizzle/schema.ts and adjust as needed. The introspection tool maps:
Table names
Column types
Constraints
Indexes
Foreign keys
Test queries
Write equivalent Drizzle queries and verify they return the same data: // Test script
const prismaUsers = await prisma . user . findMany ();
const drizzleUsers = await db . select (). from ( usersTable );
console . log ( 'Match:' , JSON . stringify ( prismaUsers ) === JSON . stringify ( drizzleUsers ));
Handling Prisma-Specific Features
@updatedAt
Prisma’s @updatedAt automatically updates timestamps. In Drizzle:
import { timestamp } from 'drizzle-orm/pg-core' ;
export const users = pgTable ( 'User' , {
updatedAt: timestamp ( 'updatedAt' )
. notNull ()
. defaultNow ()
. $onUpdate (() => new Date ()),
});
@default(uuid())
import { uuid } from 'drizzle-orm/pg-core' ;
export const users = pgTable ( 'User' , {
id: uuid ( 'id' ). defaultRandom (). primaryKey (),
});
@default(cuid())
Drizzle doesn’t have built-in CUID. Use a library:
import { text } from 'drizzle-orm/pg-core' ;
import { createId } from '@paralleldrive/cuid2' ;
export const users = pgTable ( 'User' , {
id: text ( 'id' ). $defaultFn (() => createId ()). primaryKey (),
});
Drizzle Kit vs Prisma Migrate
Feature Prisma Migrate Drizzle Kit Auto-generate migrations Yes Yes Push schema without migrations db pushdrizzle-kit pushMigration history _prisma_migrations table__drizzle_migrations tableIntrospection prisma db pulldrizzle-kit introspectStudio (GUI) Prisma Studio Drizzle Studio
Migration Commands
# Generate migration
prisma migrate dev --name add_users
# Apply migrations
prisma migrate deploy
# Reset database
prisma migrate reset
Common Pitfalls
No automatic ID generation : Unlike Prisma’s @default(autoincrement()), Drizzle requires explicit specification:// Use serial() for auto-increment
id : serial ( 'id' ). primaryKey ()
// Or uuid()
id : uuid ( 'id' ). defaultRandom (). primaryKey ()
Explicit table names : Drizzle uses the first argument as table name:// This creates a table named "User" (matches Prisma)
export const users = pgTable ( 'User' , { ... });
// Variable name doesn't affect table name
export const usersTable = pgTable ( 'users' , { ... });
Relations are optional : Foreign keys work without defining relations. Relations are only needed for the relational query API:// This works fine
export const posts = pgTable ( 'posts' , {
userId: integer ( 'userId' ). references (() => users . id ),
});
// Relations only needed for db.query.posts.findMany({ with: ... })
export const postsRelations = relations ( posts , ({ one }) => ({ ... }));
Type Inference Differences
Drizzle provides direct type inference without code generation:
// Prisma: Generated in node_modules/.prisma/client
type User = Prisma . User ;
// Drizzle: Inferred from schema
import { users } from './schema' ;
type User = typeof users . $inferSelect ;
type NewUser = typeof users . $inferInsert ;
Drizzle typically performs better than Prisma because:
No query engine : Direct database driver usage
Smaller bundle : No Prisma engines to download
Prepared statements : Built-in support reduces parsing overhead
Edge runtime : Works in Cloudflare Workers, Vercel Edge
Benchmark example (Next.js API route):
// Prisma: ~150ms cold start
import { PrismaClient } from '@prisma/client' ;
const prisma = new PrismaClient ();
// Drizzle: ~50ms cold start
import { drizzle } from 'drizzle-orm/neon-http' ;
const db = drizzle ({ connection: process . env . DATABASE_URL ! });
Complete Example: Before & After
Prisma (before)
Drizzle (after)
import { PrismaClient } from '@prisma/client' ;
const prisma = new PrismaClient ();
export async function getUsers ( role : string ) {
return await prisma . user . findMany ({
where: { role },
include: {
posts: {
orderBy: { createdAt: 'desc' },
take: 10 ,
},
},
orderBy: { name: 'asc' },
});
}
export async function createUser ( data : {
email : string ;
name : string ;
role : string ;
}) {
return await prisma . user . create ({ data });
}
Next Steps
Schema Design Learn Drizzle schema syntax in depth
Query API Master Drizzle’s query builder
Best Practices Optimize your Drizzle implementation
Relational Queries Use the relational query API