All posts
postgresdrizzletypescript

Why this starter uses Drizzle instead of Prisma

Both are good. Here's the specific tradeoff that made Drizzle the default for a starter meant to be read, not just run.

Prisma and Drizzle solve the same problem well. The choice here came down to one thing: what the schema looks like when you open the file.

Plain SQL you can actually read

Drizzle's schema is TypeScript that compiles to the SQL you'd have written by hand:

export const user = pgTable("user", {
  id: text("id").primaryKey(),
  email: text("email").notNull().unique(),
  role: text("role", { enum: ["user", "admin"] }).notNull().default("user"),
});

No custom DSL, no generated client to regenerate after every edit, no schema file that only a bundler-aware tool can parse. grep still works. That matters a lot in a starter meant to be forked, read end-to-end, and modified by someone who's never seen the codebase before.

Migrations you can read in a PR

npm run db:generate   # writes drizzle/NNNN_name.sql
npm run db:migrate    # applies pending migrations

The generated .sql file is the actual diff — reviewable in a pull request the same way application code is, not a binary lockfile you trust blindly.

Where Prisma still wins

Prisma's relation API and its Studio GUI are genuinely nicer for deeply nested queries and non-technical schema browsing. If your team is bigger than one and half of it isn't comfortable in SQL, that tradeoff might flip back in Prisma's favor — this starter's choice isn't a universal claim, just the right default for this shape of project.