Jean Galea

AI, Investing, Health, and Building Businesses

  • Start Here
  • Guides
    • Beginner’s Guide to Investing
    • Cryptocurrencies
    • Stocks
    • P2P Lending
    • Real Estate
  • Blog
  • My Story
  • Projects
  • Community
  • AI Consultancy
  • Search

From WPDB to Drizzle, Prisma, and Kysely

Published: June 02, 2026Leave a Comment

Code editor showing typed database queries

This is part of an ongoing series I am writing as I work my way through the modern web stack from a WordPress developer’s perspective. The series is aimed at WordPress veterans who, like me, have built things on the web for years and feel quietly behind the curve. The goal is broad literacy, not deep mastery. By the end you should be able to read any modern stack list and know what each piece is doing.

Each post comes with an audio companion (10-15 minutes, generated via NotebookLM) for gym or commute listening. Press play below if that suits you better than reading.

Hook

Every WordPress developer who has been around the block has the same relationship with WPDB. You learn get_results() and prepare() early. You write raw SQL because it works. You eventually learn the bad-template pattern of $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: thin wrapper, raw SQL, no opinions, no surprises.

The modern stack went the other direction. 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 with Laravel) were heavy and slow and produced queries that looked correct but performed badly. The JavaScript ecosystem watched that and built a new generation of ORMs that are lighter, faster, and type-safe by default.

This module is about what an ORM actually does, why modern developers reach for one without thinking, and how Drizzle, Prisma, and Kysely differ from each other and from WPDB.

Core Concept

An ORM has one job: make it possible to query a database from your application code without writing raw SQL strings. Three things flow from that.

Type safety. The ORM knows your schema. Your code editor knows what tables exist, what columns they have, and what types those columns hold. Try to 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 parameterisation. This is the same protection WPDB’s prepare() gives you, plus dramatically more.

Migrations. The ORM tracks your schema definitions as code. When you change a column, the ORM generates a migration script. You apply migrations across environments (local, staging, prod) without writing the ALTER TABLE statements by hand. WordPress has no equivalent; theme and plugin developers historically just shipped raw dbDelta() calls.

A simple example, in Drizzle:

<span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> { eq } <span class="im">from</span> <span class="st">'drizzle-orm'</span><span class="op">;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> { db } <span class="im">from</span> <span class="st">'./db'</span><span class="op">;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> { users } <span class="im">from</span> <span class="st">'./schema'</span><span class="op">;</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> adminUsers <span class="op">=</span> <span class="cf">await</span> db<span class="op">.</span><span class="fu">select</span>()</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>  <span class="op">.</span><span class="fu">from</span>(users)</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>  <span class="op">.</span><span class="fu">where</span>(<span class="fu">eq</span>(users<span class="op">.</span><span class="at">role</span><span class="op">,</span> <span class="st">'admin'</span>))<span class="op">;</span></span>

The equivalent in WPDB:

<span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="va">$admin_users</span> <span class="op">=</span> <span class="va">$wpdb</span>->get_results(</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>  <span class="va">$wpdb</span>->prepare(<span class="st">"SELECT * FROM </span>{<span class="va">$wpdb</span>->users}<span class="st"> WHERE role = %s"</span><span class="ot">,</span> <span class="st">'admin'</span>)</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>)<span class="ot">;</span></span>

The Drizzle version is slightly more verbose, but the editor knows that users.role exists and is a string. If you rename the column to user_role in the schema, every query that referenced it lights up red in the editor. WPDB cannot give you that.

The line in the modern stack is: write SQL only when the ORM cannot express what you need. Most of the time it can.

The WordPress Analogue

Mental model: Drizzle, Prisma, and Kysely are to your database 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 couple of convenience methods. Modern ORMs are a layer above that: they understand your schema as code, generate the SQL for you (or let you write it explicitly when needed), and catch errors at compile time rather than at runtime.

The other thing modern ORMs do that WPDB does not: migrations. WordPress has nothing equivalent to “run drizzle-kit generate and commit the SQL file.” Theme and plugin developers ship schema changes through dbDelta(), hope, and prayer. Modern apps track schema changes alongside code in version control. The discipline shift is real.

The Landscape

There are three serious contenders in the JavaScript ORM space. They have different trade-offs.

Drizzle. SQL-first ORM, written in TypeScript. You describe your schema as TypeScript objects. Queries look almost identical to SQL but are type-safe. No runtime overhead (the queries are templates, not heavy abstractions). The default choice in 2026 for new projects, especially with Postgres on Neon or Supabase.

Prisma. The biggest, most established ORM in the JS world. Schema is described in a custom file format (schema.prisma). The Prisma Client is generated from that file and provides a great query-builder API. The runtime overhead is larger than Drizzle (Prisma ships a Rust binary that handles query parsing), which matters in serverless environments. 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. No schema definition, no migrations bundled; you bring those separately. Maximum control, minimum opinion. The choice for 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

My take, after working through both AgentVania and several side projects: Drizzle for most new work, Prisma if you are inheriting a Prisma codebase or your team strongly prefers the higher-level abstractions, Kysely if you really want raw control with types on top.

A few honourable mentions. TypeORM and Sequelize were the previous-generation JS ORMs. They still work but feel like Doctrine or Eloquent — heavier, less type-safe, less aligned with modern serverless patterns. New projects rarely reach for them.

What This Changes for WordPress People

Three practical wins once you understand the ORM model.

The first is migrations stop being a nightmare. The 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, rollbacks are explicit. If you have ever had a custom plugin’s database table get out of sync between staging and prod, you know why this matters.

The second is reading modern codebases gets easier. A Drizzle or Prisma schema file is a complete map of what your database looks like. Open schema.ts, and you can see every table, every column, every relationship. WPDB-style codebases require you to dig through phpMyAdmin, plugin install files, and forgotten dbDelta() calls to figure out the shape.

The third is AI-paired development gets dramatically better. AI agents are extremely good at writing ORM code because the type system lets them check their work. Hand an agent a Drizzle schema and ask it to “add a function that returns active subscriptions for a user,” and it will produce correct, idiomatic code almost every time. The same prompt against WPDB produces code that compiles but might be subtly wrong.

Watch Out

A few traps.

ORMs that overstep get expensive. Heavy ORMs (Prisma, older versions of TypeORM) generate queries that look fine but perform terribly under load. The N+1 query problem is the classic one: you fetch a list of users, then for each user the ORM lazily fetches their posts, resulting in 101 queries instead of 2. Modern ORMs make explicit joins and includes mandatory; learn how your ORM expresses them.

The schema-as-code workflow takes adjustment. WordPress conditioned us to change schemas via 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 breaks if you do not run it. Drizzle and Prisma both require you to regenerate types after schema changes. Drizzle Kit handles this on drizzle-kit generate. Prisma uses prisma generate. Forget to run either, and your editor lies to you about what fields exist.

Production migrations need care. Local migrations are cheap; production migrations on a table with a million rows are not. Adding a non-null column, renaming a column, dropping an index — each one needs a migration strategy that does not lock the table for ten minutes. Read your ORM’s deployment guide before your first production migration, not during.

Raw SQL is still an option. Every modern ORM has an escape hatch (db.execute(sql"...") in Drizzle, prisma.$queryRaw in Prisma). Use it when the ORM cannot express what you need. The goal is type safety and migrations, not abandoning SQL.

Going Deeper

If you want to internalise ORM patterns rather than just recognise them, a few resources worth your time.

YouTube, gym or commute friendly:

  • “Drizzle ORM in 100 Seconds” by Fireship, then “Drizzle Tutorial” on Drizzle’s YouTube channel (~30 min). The shorter one for orientation, the longer one for the actual workflow.
  • “Why I switched from Prisma to Drizzle” by Theo (t3.gg, ~20 min). Useful even if you stay on Prisma; it crystallises the trade-offs.

Official docs worth bookmarking:

  • Drizzle docs. Strong tutorial, clean reference. The “Drizzle Kit” pages are where the migration workflow lives.
  • Prisma docs. The most polished documentation in the JS database world. Worth reading even if you go with Drizzle, because Prisma sets the bar for DX.
  • Kysely docs. Short, opinionated, technical. Good once you have the basic ORM model in your head.

Next post in the series tackles authentication: what replaced wp_users, why JWT is suddenly everywhere, and when to reach for Clerk, Auth.js, or Supabase Auth.

Related

Beyond Shared MySQL: Cloud Databases for WordPress Developers
Why Modern Web Feels Alien to WordPress Devs
Next.js: The WordPress of React
JavaScript and TypeScript for PHP People

Filed under: General

About Jean Galea

I build things on the internet and write about AI, investing, health, and how to live well. Founder of AgentVania and the Good Life Collective.

Leave a Reply Cancel reply

Thanks for choosing to leave a comment. Please keep in mind that all comments are moderated according to our comment policy, and your email address will NOT be published. Please Do NOT use keywords or links in the name field.

Latest Padel Match

Jean Galea

Investor | Dad | Global Citizen | Athlete

Follow @jeangalea

  • My Padel Journey
  • Affiliate Disclaimer
  • Cookies
  • Contact

Copyright © 2006 - 2026