Why I Decided to Part Ways with Prisma
After more than half a year of using Drizzle ORM in a real-world SaaS project, I’ve learned one lesson: sometimes minimalism is the ultimate sophistication. Previously, my team was very fond of Prisma due to its convenience. However, as the project grew, cracks began to show. Cold starts on Vercel sometimes spiked to over 2 seconds because Prisma’s binary files were too heavy. Additionally, the schema.prisma file grew to 1,500 lines, making tracking changes via Git a total nightmare.
Drizzle ORM arrived and solved exactly what I needed. It doesn’t create a new abstract language. Instead, Drizzle stays close to raw SQL syntax while still ensuring 100% Type-safety. The biggest plus is that it’s extremely lightweight. No binaries, no overhead—its query speed is almost equivalent to using a native driver.
The 3 Core Pillars of Drizzle ORM
To master this tool, you only need to understand how these three components work:
- Drizzle ORM: The core library for writing queries. It acts as an intelligent and flexible query builder.
- Drizzle Kit: A CLI tool for migration management. It compares your
.tscode files with the current database to automatically generate SQL files. - Database Driver: Drizzle doesn’t connect to the DB directly. It leverages existing drivers like
postgres.jsormysql2to optimize performance.
Unlike Prisma’s approach, Drizzle defines the schema using TypeScript code itself. This allows you to leverage the power of variables, functions, and programming logic to break the schema into manageable modules.
Implementation Guide: From Installation to Your First Query
Here is the PostgreSQL setup process I typically use for real-world projects. This process has been streamlined to save as much time as possible.
Step 1: Initialization and Installation
Start by installing the necessary packages. I chose postgres.js because it offers superior performance compared to the older pg library.
mkdir drizzle-demo && cd drizzle-demo
npm init -y
npm install drizzle-orm postgres
npm install -D typescript drizzle-kit @types/node
npx tsc --init
Step 2: Defining the Schema with TypeScript
Create the src/db/schema.ts file. Writing the schema here is great because the IDE provides instant code suggestions. You don’t need to install any separate plugins.
import { pgTable, serial, text, varchar, timestamp } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: serial("id").primaryKey(),
fullName: text("full_name").notNull(),
email: varchar("email", { length: 255 }).notNull().unique(),
createdAt: timestamp("created_at").defaultNow(),
});
export const posts = pgTable("posts", {
id: serial("id").primaryKey(),
title: text("title").notNull(),
content: text("content").notNull(),
authorId: serial("author_id").references(() => users.id),
});
The advantage here is modularity. You can separate each table into its own file and import them back—extremely clean for projects with dozens of tables.
Step 3: Configuring Migrations
Create a drizzle.config.ts file in the root directory. This file directs Drizzle Kit to find the schema and the location for storing change history.
import { defineConfig } from "drizzle-kit";
export default defineConfig({
schema: "./src/db/schema.ts",
out: "./drizzle",
dialect: "postgresql",
dbCredentials: {
url: process.env.DATABASE_URL || "postgresql://user:pass@localhost:5432/db",
},
});
To generate the migration file, run the following command:
npx drizzle-kit generate
Drizzle will generate raw SQL files. You can open, inspect, and edit them directly. This is much more transparent than how Prisma manages things under the hood.
Step 4: Performing CRUD Operations
Test your first query in src/index.ts. Everything is Type-safe without needing to run a manual generate command.
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
import { users } from "./db/schema";
import { eq } from "drizzle-orm";
const queryClient = postgres("postgresql://user:pass@localhost:5432/db");
const db = drizzle(queryClient);
async function run() {
// Add new data
await db.insert(users).values({
fullName: "Hoang Code",
email: "[email protected]",
});
// Fetch data with full TS autocompletion
const result = await db.select().from(users);
console.log(result);
}
run();
Lessons Learned After 6 Months in Production
Applying Drizzle significantly increased my team’s productivity. There are three practical points I want to emphasize:
- Tiny bundle size: My application reduced its size by about 15MB when deployed to Lambda. Cold starts dropped from ~2s to under 300ms.
- Easy debugging: If a query is slow, I just use the
.toSQL()function. It prints the actual SQL statement for me to optimize (using Explain Analyze). - Flexible but requires caution: Drizzle Kit sometimes handles column renames less intelligently than Prisma. In those cases, I usually manually edit the SQL migration files to ensure data safety.
Conclusion
Drizzle ORM is not a “silver bullet” to completely replace Prisma. If you prioritize absolute stability and a large community, Prisma is still a great choice. But if you need speed, lightweight performance, and want deep control over your SQL, give Drizzle a try. Start with a few small microservices or a personal project to feel the difference before rolling it out at scale.

