
This is part of a series on the modern web stack, written from a WordPress developer’s seat for people who’ve built on the web for years and still hit stack terms they can’t define. The goal is literacy, enough that you can read any stack list and know what each piece does.
Each post has a 10-15 minute audio companion, generated with NotebookLM, for the gym or the commute.
Every WordPress developer who’s been around has the same relationship with WPDB. You learn get_results() and prepare() early, write raw SQL because it works, and eventually pick up $wpdb->insert() with associative arrays. You never go further because you never need to. WordPress was built when MySQL was the only database, and WPDB reflects that: a thin wrapper, raw SQL, no opinions, no surprises.
The modern stack went the other direction. Nearly every serious app in 2026 talks to its database through an Object Relational Mapper. ORMs gave PHP developers heartburn for fifteen years because the early ones (Doctrine, Eloquent in Laravel) were heavy, slow and produced queries that looked correct but performed badly. The JavaScript ecosystem watched that and built a new generation that’s lighter, faster and type-safe by default.
What follows covers what an ORM does, why modern developers reach for one without thinking, and how Drizzle, Prisma and Kysely differ from each other and from WPDB. If you’re still sorting out where your data lives once you leave shared MySQL, beyond shared MySQL covers the database side.
What an ORM Does
An ORM lets you query a database from application code without writing raw SQL strings. Three things follow from that.
Type safety
The ORM knows your schema, and so does your editor: which tables exist, which columns they have, what types those columns hold. Write db.users.findMany({ where: { emial: '...' } }) and the compiler catches the typo before the code runs.
Query construction
Instead of building SQL strings with placeholders, you compose queries from method calls. The ORM handles escaping, joining and parameterization. That’s the same protection prepare() gives you in WPDB, plus a lot more.
Migrations
The ORM tracks your schema definitions as code. When you change a column, it generates a migration script, and you apply that script across local, staging and production without writing ALTER TABLE by hand. WordPress has no equivalent. Plugin developers have historically shipped raw dbDelta() calls.
A simple example in Drizzle:
import { eq } from 'drizzle-orm';
import { db } from './db';
import { users } from './schema';
const adminUsers = await db.select()
.from(users)
.where(eq(users.role, 'admin'));
The equivalent in WPDB:
$admin_users = $wpdb->get_results(
$wpdb->prepare("SELECT * FROM {$wpdb->users} WHERE role = %s", 'admin')
);
The Drizzle version is slightly longer, but the editor knows users.role exists and is a string. Rename the column to user_role in the schema and every query that referenced it lights up red. WPDB can’t do that.
The working rule in the modern stack: write SQL only when the ORM can’t express what you need. Most of the time it can.
The WordPress Analogue
Drizzle, Prisma and Kysely are what WPDB tried to be, with the corners filed off and a type system bolted on.
WPDB is a thin wrapper over
mysqli. It gives you basic escaping and a few convenience methods. Modern ORMs sit a layer above: they understand your schema as code, generate the SQL for you (or let you write it explicitly), and catch errors at compile time instead of at runtime.They also do migrations, which WPDB doesn’t. WordPress has nothing like “run
drizzle-kit generateand commit the SQL file.” Modern apps track schema changes in version control next to the code, and that’s a real shift in discipline.
The Three ORMs Worth Knowing
Three serious contenders exist in the JavaScript ORM space, each with different trade-offs.
Drizzle
A SQL-first ORM written in TypeScript. You describe your schema as TypeScript objects, and queries look almost identical to SQL but are type-safe. There’s no runtime overhead to speak of, since queries are templates and not heavy abstractions. It’s my default for new projects in 2026, especially with Postgres on Neon or Supabase.
Prisma
The biggest, most established ORM in the JS world. The schema lives in a custom file format (schema.prisma), and Prisma Client is generated from it with a very good query-builder API. Runtime overhead is larger than Drizzle’s because Prisma ships a Rust binary that handles query parsing, which matters in serverless environments (serverless and edge explained covers why). The newer Prisma Edge version addresses this but is still maturing.
Kysely
A pure type-safe query builder, closer to “WPDB with types” than to a full ORM. It has no schema definition and no bundled migrations, so you bring those separately. You get maximum control and minimum opinion, which suits developers who want type safety but resent abstractions.
| ORM | Schema | Migrations | Runtime overhead | Strength | Trade-off |
|---|---|---|---|---|---|
| Drizzle | TypeScript objects | Drizzle Kit | Minimal | Closest to SQL, type-safe, serverless-friendly | Newer, smaller community than Prisma |
| Prisma | Custom .prisma schema |
Built-in | Larger (Rust binary) | Polished DX, mature, huge ecosystem | Heavier in serverless, opinionated abstractions |
| Kysely | None bundled | Bring your own | Tiny | Query-builder freedom, no opinions | More boilerplate, fewer batteries included |
After working through AgentVania and several side projects, I reach for Drizzle on most new work. Prisma makes sense if you’re inheriting a Prisma codebase or your team prefers higher-level abstractions, and Kysely if you want raw control with types on top.
TypeORM and Sequelize were the previous generation of JS ORMs. They still work, but they feel like Doctrine or Eloquent: heavier, less type-safe, less suited to serverless. New projects rarely pick them.
What This Changes for WordPress People
Migrations stop being a nightmare
WordPress culture around schema changes is essentially “ship a dbDelta() and pray.” Modern ORMs make migrations a first-class concept: every schema change generates a migration file, every environment runs the same migrations in the same order, and rollbacks are explicit. If a custom plugin’s table has ever drifted out of sync between staging and prod on you, you know why that matters.
Modern codebases get easier to read
A Drizzle or Prisma schema file is a complete map of your database. Open schema.ts and you see every table, column and relationship. A WPDB-style codebase makes you dig through phpMyAdmin, plugin install files and forgotten dbDelta() calls to work out the shape.
AI-paired development improves
AI agents write ORM code well because the type system lets them check their work. Hand an agent a Drizzle schema and ask for “a function that returns active subscriptions for a user,” and it’ll produce correct, idiomatic code almost every time. The same prompt against WPDB produces code that compiles but may be subtly wrong.
Where ORMs Get Expensive
ORMs that overstep
Heavy ORMs (Prisma, older versions of TypeORM) can generate queries that look fine and perform terribly under load. The classic case is the N+1 problem: you fetch a list of users, then the ORM lazily fetches each user’s posts, and you get 101 queries instead of 2. Learn how your ORM expresses explicit joins and includes.
The schema-as-code workflow
WordPress trained us to change schemas through phpMyAdmin or dbDelta() calls in plugin code. Modern projects expect you to edit a schema file, run a CLI command to generate a migration, and commit both. Skip the CLI step and your local schema drifts from your team’s. Treat the schema file as the source of truth.
Type generation
Drizzle and Prisma both need types regenerated after schema changes: drizzle-kit generate for Drizzle, prisma generate for Prisma. Forget to run either and your editor misreports which fields exist.
Production migrations
Local migrations are cheap. Production migrations on a table with a million rows aren’t. Adding a non-null column, renaming a column or dropping an index each needs a strategy that won’t lock the table for ten minutes. Read your ORM’s deployment guide before your first production migration, not during it.
Raw SQL is still available
Every modern ORM has an escape hatch (db.execute(sql"...") in Drizzle, prisma.$queryRaw in Prisma). Use it when the ORM can’t express what you need. What you want from an ORM is type safety and migrations, and you can keep writing SQL alongside them.
Go Deeper on ORMs
If you want to absorb ORM patterns and not just recognize them, these are worth your time.
YouTube
- “Drizzle ORM in 100 Seconds” by Fireship, then “Drizzle Tutorial” on Drizzle’s YouTube channel (about 30 minutes). The short one orients you, the long one shows the workflow.
- “Why I switched from Prisma to Drizzle” by Theo (about 20 minutes). Useful even if you stay on Prisma, since it lays out the trade-offs.
Official docs
- Drizzle docs: a strong tutorial and a clean reference. The Drizzle Kit pages hold the migration workflow.
- Prisma docs: the most polished documentation in the JS database world, and worth reading even if you choose Drizzle.
- Kysely docs: short, opinionated and technical. Best once you have the basic ORM model in your head.
The next post covers auth in 2026: what replaced wp_users, why JWT is everywhere, and when to reach for Clerk, Auth.js or Supabase Auth.

Leave a Reply