The library that lets you talk to your database in your application's language — with type checking, schema migrations, and an API that mostly looks like the rest of your code.
Research-based overview. This article synthesizes public documentation, pricing pages, and user reports. How we research.
An ORM (Object-Relational Mapper) is a library that maps rows in a relational database to objects in your application code. Instead of writing SQL strings and manually parsing the result rows, you call methods on a typed model and the library handles the translation in both directions. Concretely:
// Raw SQL
const result = await db.query(
'SELECT id, email, created_at FROM users WHERE id = $1',
[userId]
);
const user = result.rows[0];
// user is { id: number, email: string, created_at: Date }
// but TypeScript doesn't know that
// ORM (Prisma)
const user = await prisma.user.findUnique({
where: { id: userId }
});
// user is fully typed: User | null
// the WHERE, the SELECT, and the type all match the schema
That's the whole pitch in one example. You stop writing strings; you start calling methods. The library generates the SQL, runs it, parses the result, and hands you a typed object. The history of this idea goes back to the late 1990s — the term object-relational mapping was popularized by frameworks like Hibernate (Java) and ActiveRecord (Ruby on Rails) — but the modern JavaScript/TypeScript wave has reshaped what an ORM looks like.
Three problems an ORM is built to solve, in roughly the order developers feel them:
Developer ergonomics. Writing raw SQL means concatenating strings, escaping inputs, and manually mapping result rows to your domain types. An ORM gives you autocomplete, parameterization, and a query API that looks like the rest of your code.
Type safety. A raw query returns any until you cast it. A modern ORM derives query types from your schema, so a typo in a column name is a compile error, not a runtime crash. This is the single biggest reason TypeScript developers reach for an ORM.
Schema evolution. Your database schema changes over time — new columns, renamed tables, dropped indexes. ORMs ship migration tooling: a way to express schema changes as code, version-control them, and apply them in order across environments.
Nothing is free. The same abstractions that buy you ergonomics cost you in three places:
db.select().from(users).where(eq(users.id, x)) chain. That investment is real.| Tool | Style | Strengths | Where it loses |
|---|---|---|---|
| Prisma | Schema-first; generated client | Best DX, excellent docs, mature migration tooling | Bundle size; historically slow at the edge (improving) |
| Drizzle | Code-first; SQL-like query builder | Tiny bundle, edge-ready, queries look like SQL | Smaller community, more verbose for simple cases |
| Kysely | Pure query builder, not an ORM | Type-safe SQL with no abstraction tax | You write more code; no migrations or model layer |
| TypeORM | Class-based, decorator-heavy | Familiar to Java/.NET developers | Aging API; less momentum than Prisma or Drizzle |
| MikroORM | Unit-of-work pattern | Identity map, change tracking, transactions done right | Niche; high concept count for solo founders |
For most solo SaaS founders building a Next.js app today, the real choice narrows to Prisma vs Drizzle. Prisma still wins on developer experience for greenfield projects; Drizzle wins when you care about cold starts or want SQL-shaped queries. The full breakdown is in Prisma vs Drizzle.
These three sit on a spectrum. ORMs offer the highest abstraction, raw SQL offers the lowest, and query builders sit in the middle. Some heuristics for picking:
Pick an ORM when you're building a CRUD-heavy SaaS where most queries are simple finds, creates, updates, and deletes; you value the migration tooling; and your team is small enough that consistency matters more than maximum performance. This describes most solo SaaS apps.
Pick a query builder (Kysely, the SQL DSL inside Drizzle) when you write enough complex SQL that the ORM keeps getting in your way, but you still want type safety on your column names. Query builders give you SQL-shaped code with TypeScript checking on the schema.
Pick raw SQL when you're writing analytical queries that don't fit the row-as-object model, when performance matters more than developer ergonomics, or when the query is just shorter as SQL. There's no reason to ship 100% of your code through one of these layers; mixing is fine.
Most production codebases end up using all three: an ORM for the 90% of queries that are CRUD, raw SQL for the 5% that are reporting or analytics, and migration tooling that's bundled with the ORM. This is also the pattern AI coding tools tend to generate when scaffolding a new project.
This is the part most ORM articles skip. Your ORM choice ripples into where you can deploy and how fast your cold starts will be.
Vercel Edge Functions and Cloudflare Workers have hard bundle size limits (1 MB on Cloudflare's free tier, larger paid). Prisma's generated client used to be too large for the edge; recent versions have a smaller edge-compatible variant but you still need to use it deliberately. Drizzle is small enough that it works at the edge with no special handling. If your plan is to run database queries from edge functions, this is a serious selection criterion. See what is an edge function for context.
On AWS Lambda, the time from request to function-ready includes loading the ORM, opening a database connection, and parsing the schema. Prisma historically added 200–500 ms here; Drizzle adds 20–50 ms. With serverless Postgres providers, the database side also adds cold-start latency. Stack the two and you've got perceptible delays on the first request after idle.
Postgres opens a process per connection. Each serverless invocation that opens a fresh connection eats into the database's connection limit. ORMs differ in how they handle this: most rely on you to put a pooler (PgBouncer, Supavisor, Neon's pooler) in front. Supabase and Neon both ship with poolers; if you're hosting Postgres yourself, you'll add one.
If you start from a SaaS boilerplate, the ORM is usually pre-chosen. Most popular boilerplates ship with Prisma; a smaller subset use Drizzle. Switching afterward is a real refactor, not a config change. Worth checking before you commit. Our take is in best Next.js SaaS boilerplate.
The pragmatic default. For a new solo-founder SaaS in 2026: Prisma if you're optimizing for development speed; Drizzle if you're optimizing for edge deployment or cold-start latency. Either is a reasonable choice. The wrong choice is no ORM at all on a TypeScript codebase — that path leads to runtime errors that should have been compile errors.
The stack, prompts, pricing, and mistakes to avoid — for solo founders building with AI.