Engineering Playbook
Databases

ORMs

Prisma, Drizzle, TypeORM, and the Active Record vs Data Mapper debate.

Object-Relational Mappers (ORMs)

SQL is powerful, but mapping rows to Objects/Types manually is tedious. ORMs automate this.

The Spectrum

1. Active Record (Rails, Eloquent, TypeORM)

The Model is the Database Row. It has methods to save itself.

const user = new User();
user.name = "Alice";
user.save(); // The object knows how to connect to DB
  • Pros: Fast development, intuitive.
  • Cons: Violates Single Responsibility Principle. Mixing Logic and Persistence. Hard to test.

2. Data Mapper (Hibernate, MikroORM)

The Model is a dumb class. A separate "Repository" or "Manager" handles saving.

const user = new User("Alice");
repo.save(user);
  • Pros: Pure domain objects. Clean architecture friendly.
  • Cons: More boilerplate.

3. The Modern Query Builder (Prisma, Drizzle)

Not strictly an ORM, but a type-safe layer over SQL.

  • Prisma: Generates a massive TypeScript client based on schema.prisma. Heavy runtime.
  • Drizzle: "If you know SQL, you know Drizzle." Lightweight, almost zero runtime overhead.

The Danger Zone

The N+1 Problem

ORMs make it easy to accidentally DDOS your database.

const users = await User.find(); // 1 Query
users.forEach(u => {
  console.log(u.posts); // Hidden Query per user!
});

Fix: Eager Loading. User.find({ include: { posts: true } }).

Leaky Abstractions

ORMs try to hide SQL, but they fail for complex queries.

  • Eventually, you will need to write raw SQL (prisma.$queryRaw).
  • If you rely 100% on the ORM, you will write inefficient queries because you don't understand what the ORM is generating under the hood.

Opinion

Drizzle ORM is currently the sweet spot for TypeScript backends. It gives you type safety and migrations without hiding the SQL reality from you.