Doron Segal Founder · CTO · YC W21 Book a call
NODEJS · POSTGRESQL · SOFTWARE-ENGINEERING · Nov 12, 2025 · 4 min read

Stop Using ORMs in Node.js — They’re Slowing You Down, Not Helping

I’ve spent the last decade writing Node.js at scale from small startups to high-traffic platforms processing millions of transactions.

And there’s one mistake I see again and again:

Developers treating ORMs like a safety net… when in reality, they’re a straitjacket.

Every few months, there’s a shiny new ORM that promises to “make SQL disappear.”

But after the hype fades, you’re left with performance issues, broken migrations, and dependencies older than your coffee.

Here’s why you should stop using ORMs and what to use instead.

🧠 The Big Lie of ORMs

Object-Relational Mappers (ORMs) were built to make working with databases “easy.”

You define models, and it magically builds tables and queries for you.

That works fine until you actually care about performance, security, or control.

Then the magic becomes quicksand.

⚙️ 1. Performance That’s Never What You Think

ORMs love to generate SQL that no human would ever write bloated, inefficient queries with 8 joins and 0 indexes.

You ask for user.orders.items, and get a Frankenstein query that makes your DBA cry.

Performance tuning becomes guesswork, because you’re not even sure what SQL is being executed.

At scale, that’s death by abstraction.

🧩 2. SQL Is Not the Enemy. It’s the Superpower

PostgreSQL is one of the most powerful databases in the world.

It has CTEs, window functions, JSONB, and materialized views that can replace half your backend logic.

ORMs flatten all of that into “model.findMany()”.

They steal your best tool and give you a toy hammer in return.

SQL isn’t the problem — it’s the power you’ve been told to fear.

💀 3. Migrations That Age Like Milk

Automatic migrations sound cool… until they go rogue.

Rename a column? Add a constraint? The ORM might “guess” what you meant and break production.

With real migrations (like node-pg-migrate), you control every CREATE, ALTER, and DROP.

No magic. No surprises. Just clean, versioned schema changes.

🔒 4. Security Risks Nobody Talks About

Every ORM brings a web of transitive dependencies — packages you didn’t install, but you’re still trusting.

And every year, some of them get compromised or quietly abandoned.

When you rely on libraries with hundreds of dependencies, you’re inviting supply-chain risk.

Using pg the official PostgreSQL driver, means:

  • Fewer dependencies 🧹
  • Fewer attack surfaces 🔐
  • Fewer surprises from unmaintained code 💀

Simplicity is security.

🌍 5. The Community Problem: The “New Shiny Thing” Cycle

Node.js has a cultural problem: we keep reinventing the wheel every 18 months.

Knex → Sequelize → TypeORM → Prisma → Drizzle → something new next year.

Each promises to fix the last one’s flaws.

Each ends up with its own set of issues and an inevitable “deprecated” tag.

Meanwhile, stable libraries like pg and node-pg-migrate just keep working, quietly, in production systems that never went down because of a broken ORM upgrade.

Longevity beats novelty.

🚀 The Alternative: Simple, Fast, and Maintainable

✅ Use pg for database connections

import { Pool } from 'pg'
const pool = new Pool({ connectionString: process.env.PG_URL })
const { rows } = await pool.query('SELECT * FROM users WHERE email = $1', [email])

Clean. Fast. Predictable.

✅ Use node-pg-migrate for migrations

npm install node-pg-migrate

Example migration:

exports.up = (pgm) => {
pgm.createTable('users', {
id: 'id',
email: { type: 'text', notNull: true, unique: true },
created_at: { type: 'timestamp', default: pgm.func('current_timestamp') },
})
}

Readable, versioned, and under your control.

🧠 Why This Approach Wins

  • Performance: You write SQL optimized for your schema, not someone else’s abstraction.
  • Security: Minimal dependencies = smaller attack surface.
  • Control: You own your queries, indexes, and transaction flow.
  • Stability: No framework churn every six months.
  • Transparency: Every query is visible and debuggable.
  • Readability: Your SQL is clear enough to copy-paste straight into psql and run EXPLAIN ANALYZE — no layers of abstraction to unwrap.

👀 Example: ORM vs. SQL Clarity

ORM Version:

await db.user.findMany({
include: {
orders: {
where: { status: 'completed' },
include: { items: true },
},
},
})

That looks harmless… until you check the actual query generated:

SELECT "User".*, "Order".*, "Item".*
FROM "User"
LEFT JOIN "Order" ON "Order"."user_id" = "User"."id"
LEFT JOIN "Item" ON "Item"."order_id" = "Order"."id"
WHERE "Order"."status" = 'completed';

Except the ORM might add redundant selects, aliases, and subqueries depending on your relationships.

Now imagine debugging this inside a transaction across multiple joins.

Plain SQL Version (with pg):

const result = await pool.query(`
SELECT u.id, u.name, o.id AS order_id, i.name AS item_name
FROM users u
JOIN orders o ON o.user_id = u.id
JOIN items i ON i.order_id = o.id
WHERE o.status = 'completed'
`)

Readable. Predictable. Copy, paste, EXPLAIN ANALYZE, done.

You know exactly what’s running, how it performs, and how to tune it.

💡 When in doubt, open psql not your ORM docs.

⚖️ The Tradeoff

Yes, you’ll write more SQL.

But you’ll understand every line of it and that’s the difference between “it works” and “it scales.”

Owning your data layer is not an inconvenience.

It’s a competitive advantage.

🔥 TL;DR

Stop using ORMs.

They’re fine for prototypes, but poison for systems meant to last.

Use:

  • pg for database connections
  • node-pg-migrate for migrations
  • SQL for power and clarity

PostgreSQL is an engine.

Don’t let an ORM drive it off a cliff.

✍️ About the Author

Doron Segal — Co-Founder & CTO at Per Diem (YC W21).

Built and scaled Node.js + PostgreSQL systems powering thousands of merchants and millions of transactions.

ORMs are great but going with pg and node-pg-migrate is often more secure, less dependent on fragile libraries, easier to read, and future-proof.

You’ll write cleaner code, understand your queries, and most likely won’t need to migrate to a new ORM in a couple of years.

Originally published on Medium.

Working through something like this? Tell me the problem.

Book a call

Prefer email? doron@segaldoron.com