Understanding JSON → Drizzle
A schema that's also a value.
Why Drizzle moves the schema into TypeScript, the $inferSelect trick, and where it wins over Prisma.
The schema is just TypeScript.
Drizzle takes a different stance from Prisma: instead of a separate .prisma DSL that compiles to a TS client, the schema is written directly in TypeScript as a set of typed values. pgTable("users", { id: serial("id").primaryKey(), email: text("email").unique() }) is a regular TS expression. The result is a value you can pass around — query builders take the table as input.
$inferSelect, $inferInsert.
The killer property: from one table definition, Drizzle infers two TypeScript types via typeof users.$inferSelect and typeof users.$inferInsert. The first is the shape of rows you read out (including auto-generated columns); the second is the shape you insert (with auto-generated columns optional). No separate codegen step, no client to regenerate when the schema changes — the types update the moment the schema does.
A worked example.
From { "id": 7, "email": "a@b.com", "name": "Q", "createdAt": "2026-05-13T..." }: import { pgTable, serial, text, timestamp } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: serial("id").primaryKey(),
email: text("email").notNull().unique(),
name: text("name").notNull(),
createdAt: timestamp("createdAt").defaultNow().notNull(),
});
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert; Two types fall out for free. The schema, the read type, and the insert type all stay in sync because one is the source of truth.
Read vs insert types
$inferSelect vs $inferInsert
Auto-generated fields are required on select, optional on insert.
users.$inferInsert allows id omitted; $inferSelect requires it
= Two derived types, no codegen
Where Drizzle wins.
Three places. First, no build step — every other ORM in TypeScript involves running a generator or watching for schema changes. Drizzle is just types. Second, edge runtimes — Cloudflare Workers, Vercel Edge, Bun — where bundling a Prisma client is painful, Drizzle ships a minimal SQL builder. Third, raw SQL — Drizzle's escape hatch into sql template literals is far less awkward than Prisma's, so complex queries don't require leaving the ORM behind.
Relations, the explicit version.
Drizzle's relations are declared separately from the table definitions via relations(). This is more verbose than Prisma's inline @relation attributes but more explicit about cardinality (one-to-many, many-to-many) and what to load on a given query. Codegen tools detect nested objects and emit the foreign keys; the relations file is usually a hand-edit afterwards.
When to pick Drizzle vs Prisma.
Prisma is the default if you want a migrations workflow, a GUI (Prisma Studio), and a single client for every database. Drizzle is the default for projects that value minimal magic, edge runtimes, or close-to-SQL ergonomics. Both produce correct schemas from JSON; the difference is what you're optimising for in the surrounding tooling.