Sometimes the ORM is the problem. Query builders sit one rung up from raw strings — typed, composable, parameterized — without pretending the database is a graph of objects. Many production codebases use both: an ORM for CRUD, a query builder (or plain SQL) for reporting and hot paths.
← Back to Database Side.select().from().where() instead of templating strings.The original Node query builder. Powers Bookshelf and parts of Objection.js. Maintenance has slowed; new TS code often picks Kysely or Drizzle instead.
Type-safe SQL builder for TypeScript. Schema-aware, no code generation, runs anywhere TS does. Strong fit on edge runtimes.
Generates Java code from your schema; queries are compile-checked against real columns. Premium licensing for proprietary databases; free for open-source ones.
Macro-based — sqlx::query!() validates SQL against the live database at compile time. Pure SQL strings, type-safe results.
Micro-ORM / mapper. You write SQL; Dapper materializes results into objects. The default companion to EF Core.
The expression language without the ORM. Composable SQL, full type support, no session machinery.
Fluent SQL builder. Go culture leans toward raw SQL with database/sql or sqlx; builders are an opt-in convenience.
Goose, dbmate, sqlc (Go), HugSQL (Clojure). Write SQL in .sql files; tooling wires it into typed code. The trend is rising.
Window functions, recursive CTEs, lateral joins, GROUPING SETS. ORMs translate these poorly or not at all. Express them in SQL and stop fighting the abstraction. The query lives in source control like any other code; tested with database fixtures.
ORMs generate one INSERT per object; query builders let you write a single multi-row insert or a Postgres COPY. Going from 10k inserts/sec to 100k+ usually means leaving the ORM at the door.
Pattern: ORM-managed entities for the write model; SQL-shaped queries for read endpoints. The reads are denormalized projections — they don't fit the entity graph anyway, and the ORM's tracking is pure overhead.
Postgres' jsonb operators, full-text search, ON CONFLICT, partial indexes. ORMs cover the lowest common denominator across dialects. If you've committed to one database, use it.
You've optimized the eager loads, added the indexes, profiled the queries — and the ORM still emits something pathological. The repro is a 12-line raw SQL query that runs 50x faster. Use it. Save the ceremony for queries the ORM is good at.
EXPLAIN output in the PR description for non-trivial queries..sql files — best of both worlds: SQL you can read, types the compiler enforces.