<!-- llms-explorer concept facts · https://llms-explorer.com/tree/node-js-typescript-orms-and-query-builders/ · pack 2026-09-08 · ~8422 tokens -->

# Node.js & TypeScript ORMs and Query Builders

> This reference is about the SQL data-access layer in TypeScript/Node.js: the

Parent: [JavaScript and Node.js](https://llms-explorer.com/tree/javascript-and-node-js/) · 17 facets · 101 facts · page: https://llms-explorer.com/tree/node-js-typescript-orms-and-query-builders/

## Overview

- This reference is about the SQL data-access layer in TypeScript/Node.js: the library that sits between your code and a relational database (PostgreSQL, MySQL, SQLite, SQL Server) and the patterns - migrations, N+1, transactions, pooling - that apply no matter which library you pick. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#overview)
- The field splits along one axis: how much abstraction over SQL you want. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#overview)
  - Full ORM (Prisma, TypeORM, Sequelize, MikroORM): models/entities, a relation graph, change tracking, and a high-level query API. You think in objects; the library writes SQL and maps rows back. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#overview)
  - Query builder (Kysely; Drizzle's SQL-like API): a thin, type-safe wrapper over SQL itself. You think in select/from/join; you get autocomplete and compile-time column checking but no relation/identity abstraction. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#overview)
  - Raw driver (pg, mysql2, better-sqlite3): you write SQL strings. Maximum control, zero type-safety, most boilerplate. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#overview)
- Drizzle straddles the line - it markets as an ORM but is closer to a typed query builder with an opt-in relational API. The single most consequential decision is ORM-vs-builder-vs-driver; everything else (migrations, transactions) is then a detail of the chosen tool. For MongoDB (Mongoose/ODM and document modeling) this file does not apply - see mongodb-expert. For the driver-level pool internals of one specific database, see that driver's own docs. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#overview)

## 1. Prisma — schema-first ORM with a generated client

- Prisma's center of gravity is a single declarative file, schema.prisma: datasource, generator, and model blocks define the data model in Prisma's own DSL (not TS). prisma generate reads it and emits Prisma Client - a fully typed, autocompleting query API generated into node_modules. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#1-prisma-schema-first-orm-with-a-generated-client)
  - Migrations: prisma migrate dev (development - diffs the schema, creates a SQL migration, applies it, regenerates the client) vs prisma migrate deploy (production/CI - applies already-committed migrations, never generates new ones). prisma db push skips migration files for prototyping; prisma db pull introspects an existing DB into the schema. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#1-prisma-schema-first-orm-with-a-generated-client)
  - Type-safety: the client is generated from the schema, so model shapes, select/include projections, and where filters are all statically typed - a projection returns exactly the selected fields. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#1-prisma-schema-first-orm-with-a-generated-client)
  - The engine model (important & changing): historically Prisma shipped a Rust query engine binary that the JS client talked to. Prisma is removing it - v7 (Nov 2025) makes a Rust-free client the default, using TS driver adapters over the native Node driver. This changes pooling defaults (now the driver's, not Prisma's) and improves edge/serverless fit. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#1-prisma-schema-first-orm-with-a-generated-client)
  - Relation queries: include / select with nested writes; findMany, create, nested connect/createMany. Prisma can emulate relations in the app layer via relation mode (prisma vs foreignKeys) when the DB can't enforce FKs (e.g. PlanetScale). — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#1-prisma-schema-first-orm-with-a-generated-client)
  - Fits: teams wanting maximum DX, type-safety, and a managed migration story; Postgres/MySQL apps. Doesn't fit: cases needing hand-tuned SQL control, or (pre-v7) edge runtimes where the engine binary was a problem. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#1-prisma-schema-first-orm-with-a-generated-client)

## 2. Drizzle ORM — SQL-first, no codegen, no runtime engine

- Drizzle defines the schema in TypeScript (pgTable/mysqlTable/sqliteTable + column builders); that TS file is the single source of truth for both queries and migrations. Its design claims: zero dependencies, no code-generation step, no runtime ORM engine - a thin layer over the native driver that "always outputs exactly 1 SQL query," making it lightweight and serverless/edge-ready. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#2-drizzle-orm-sql-first-no-codegen-no-runtime-engine)
  - Two query APIs: a SQL-like builder (db.select().from(users).where(...), reads like SQL) and an opt-in relational queries API (db.query.users. findMany({ with: { posts: true } })) for nested data without manual joins. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#2-drizzle-orm-sql-first-no-codegen-no-runtime-engine)
  - drizzle-kit is the CLI: generate (emit SQL migrations from schema diff), migrate (apply them), push (prototype: push schema straight to DB), pull (introspect), studio (GUI), plus check/up. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#2-drizzle-orm-sql-first-no-codegen-no-runtime-engine)
  - Transactions: await db.transaction(async (tx) => { ... }). — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#2-drizzle-orm-sql-first-no-codegen-no-runtime-engine)
  - Fits: edge/serverless (Cloudflare Workers, Vercel Edge, Neon/Turso), teams who want to see the SQL, bundle-size-sensitive deploys. Trade-off: less hand-holding than Prisma; you own more of the modeling. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#2-drizzle-orm-sql-first-no-codegen-no-runtime-engine)

## 3. Kysely — a type-safe query *builder* (not an ORM)

- Kysely is a type-safe SQL query builder inspired by Knex. It is explicitly NOT an ORM and has no concept of relations - you write SQL semantics (selectFrom, innerJoin, where, CTEs, window functions) and get full compile-time checking and autocomplete derived from a Database interface you declare (table → column-type map). — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#3-kysely-a-type-safe-query-builder-not-an-orm)
  - Type inference: column names, aliases, and result types are inferred from subqueries, joins, and with (CTE) statements - the result type has exactly the selected columns with correct types. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#3-kysely-a-type-safe-query-builder-not-an-orm)
  - Composability: everything is an Expression; SelectQueryBuilder and raw builders are themselves expressions, so you build reusable query fragments and helpers. Query building and execution can be split. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#3-kysely-a-type-safe-query-builder-not-an-orm)
  - The Database type is usually generated by kysely-codegen (official), prisma-kysely, or introspection - keeping types in sync with the real DB. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#3-kysely-a-type-safe-query-builder-not-an-orm)
  - Ships transactions (db.transaction().execute(...)), a migration framework, and an sql template-tag escape hatch. Compiles to one statement. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#3-kysely-a-type-safe-query-builder-not-an-orm)
  - When a builder beats an ORM: complex analytical SQL (window functions, recursive CTEs, set operations), reporting, or when you want the DB schema - not an object graph - to be the mental model, with zero hidden queries. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#3-kysely-a-type-safe-query-builder-not-an-orm)

## 4. TypeORM, Sequelize & MikroORM — entities, decorators, the AR/DM split

- These three are the mature, entity-based ORMs. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#4-typeorm-sequelize-mikroorm-entities-decorators-the-ardm-split)
  - Active Record vs Data Mapper (the defining axis): in Active Record the entity carries its own persistence methods (user.save(), User.find()); the model extends a base class. In Data Mapper entities are "dumb" property bags and persistence lives in separate repository classes (repo.save(user)), which scales better in large apps. TypeORM uniquely supports both; MikroORM is Data Mapper; Sequelize is Active Record. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#4-typeorm-sequelize-mikroorm-entities-decorators-the-ardm-split)
  - TypeORM: @Entity/@Column/@OneToMany/@ManyToOne decorators (needs experimentalDecorators/reflect-metadata); DataSource config; Repository + QueryBuilder. Broad DB support; the de-facto NestJS default. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#4-typeorm-sequelize-mikroorm-entities-decorators-the-ardm-split)
  - Sequelize: the oldest, most battle-tested (v6 mature, v7 modernizing TS). Model classes, init/define, associations, include-based eager loading, strong transactions, read replication, migrations via sequelize-cli. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#4-typeorm-sequelize-mikroorm-entities-decorators-the-ardm-split)
  - MikroORM: implements Data Mapper + Unit of Work + Identity Map. The Identity Map guarantees one in-memory instance per DB row within a request (an in-request cache enabling cheap identity comparison and batched ops). The Unit of Work tracks all changes via snapshot diffing and persists them in one implicit transaction on em.flush() - you mutate entities and flush once. Never share an EntityManager across requests; use RequestContext (backed by AsyncLocalStorage) for request-scoped EMs. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#4-typeorm-sequelize-mikroorm-entities-decorators-the-ardm-split)
  - Legacy note: TypeORM and Sequelize predate Prisma/Drizzle and carry larger APIs and historically weaker end-to-end type-safety; choose them for ecosystem maturity (Sequelize) or AR/DM flexibility & Nest integration (TypeORM). — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#4-typeorm-sequelize-mikroorm-entities-decorators-the-ardm-split)

## 5. Migrations strategy (cross-cutting)

- A migration is a versioned, committed, ordered change to the DB schema. Across all tools the same discipline applies: — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#5-migrations-strategy-cross-cutting)
  - Generate from a schema diff, commit the SQL, apply forward in CI/prod. Prisma: migrate dev (gen+apply locally) → migrate deploy (apply in prod). Drizzle: drizzle-kit generate → migrate. Kysely/TypeORM/Sequelize ship their own runners. MikroORM has @mikro-orm/migrations. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#5-migrations-strategy-cross-cutting)
  - push is not a migration. prisma db push / drizzle-kit push sync the schema directly with no history - fine for prototyping, never for shared/ prod environments (no rollback, no audit, easy to drift). — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#5-migrations-strategy-cross-cutting)
  - Migration maturity is a real selection factor: Prisma's shadow-DB-backed drift detection and migrate deploy are the most opinionated/mature; Drizzle and Kysely are lighter and give you raw SQL files you fully own. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#5-migrations-strategy-cross-cutting)

## 6. The N+1 problem, eager/lazy loading & DataLoader (cross-cutting)

- N+1 is the canonical data-layer performance bug: one query fetches N parent rows, then the code triggers one query per parent for a relation (N more) - 1 + N round-trips where 1–2 would do. It explodes silently under ORMs whose lazy loading fetches a relation on property access, and under GraphQL resolvers (one resolver per field per item). — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#6-the-n1-problem-eagerlazy-loading-dataloader-cross-cutting)
  - Eager loading is the primary fix: tell the ORM to load the relation up front in one query or a small fixed number - Prisma include, Drizzle with, Sequelize include, TypeORM relations/leftJoinAndSelect, MikroORM populate. Lazy loading fetches on demand (less memory, but the N+1 trap). — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#6-the-n1-problem-eagerlazy-loading-dataloader-cross-cutting)
  - DataLoader is the batching fix when eager loading isn't structurally possible (e.g. GraphQL): it coalesces the per-item key lookups within a tick into one batched query and caches within the request. Create a new DataLoader per request to avoid cross-user cache bleed. MikroORM has built-in dataloaders. This is the standard GraphQL N+1 remedy. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#6-the-n1-problem-eagerlazy-loading-dataloader-cross-cutting)

## 7. Transactions, pooling, raw SQL & repositories (cross-cutting)

- Transactions: every tool wraps a callback in a DB transaction - Prisma $transaction (array form for batched independent ops, interactive form for a callback with tx), Drizzle/Kysely db.transaction(...), TypeORM dataSource.transaction / QueryRunner, MikroORM em.transactional (or the implicit transaction em.flush() already provides). — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#7-transactions-pooling-raw-sql-repositories-cross-cutting)
- Connection pooling at the data layer: the app holds a pool of DB connections; size it to the database's connection ceiling, not to traffic. In serverless, each function instance opens its own pool, so concurrent invocations can exhaust DB limits - front the DB with an external pooler (PgBouncer in transaction mode, Prisma Accelerate, Neon/Supabase poolers). Prisma historically managed its own pool (connection_limit, pool_timeout); with v7 driver adapters, pooling defaults come from the underlying driver. For the driver-internal pool mechanics of one DB, see that driver's docs. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#7-transactions-pooling-raw-sql-repositories-cross-cutting)
- Raw-SQL escape hatch: keep one even with an ORM. Prisma $queryRaw / $executeRaw (tagged-template, parameterized) and TypedSQL; Drizzle/Kysely sql\...\` template tag; Sequelize sequelize.query; TypeORM query()`. Always parameterize - string-concatenated raw SQL is SQL injection. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#7-transactions-pooling-raw-sql-repositories-cross-cutting)
- Repository pattern: wrap data access behind a repository interface so call sites depend on a method (users.findActive()), not the ORM. TypeORM/MikroORM ship Repository objects; with Prisma/Drizzle/Kysely you write thin repo modules. This isolates the ORM choice and keeps it swappable. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#7-transactions-pooling-raw-sql-repositories-cross-cutting)
- Seeding: scripted insertion of baseline/dev data (Prisma prisma db seed via a seed script; others run a plain script against the client) - keep it idempotent and separate from migrations. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#7-transactions-pooling-raw-sql-repositories-cross-cutting)

## Library comparison & selection

- Selection guidance — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#library-comparison-selection)
  - ORM vs builder vs raw driver: rich object graph, change tracking, fast CRUD DX → ORM. Complex/analytical SQL with type-safety and no hidden queries → query builder (Kysely / Drizzle SQL-API). One hot, perf-critical path or a tiny script → raw driver. Most apps mix: an ORM for CRUD + a builder/raw for the few heavy queries. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#library-comparison-selection)
  - Prisma vs Drizzle vs Kysely vs TypeORM: — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#library-comparison-selection)
    - Best end-to-end DX + migration maturity → Prisma. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#library-comparison-selection)
    - Edge/serverless, minimal bundle, SQL-first, no codegen → Drizzle. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#library-comparison-selection)
    - You think in SQL, want builder ergonomics + types, no ORM magic → Kysely. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#library-comparison-selection)
    - NestJS, or you specifically want Active-Record or Data-Mapper choice → TypeORM; classic battle-tested ecosystem → Sequelize; Unit-of-Work / DDD identity semantics → MikroORM. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#library-comparison-selection)

## Practical patterns

- Prefer eager loading by default; reach for DataLoader only where you can't eager-load (GraphQL resolvers). Both beat lazy loading in a request hot path. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#practical-patterns)
- Use migrate deploy / drizzle-kit migrate in CI, never db push / kit push, against shared environments - commit the generated SQL. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#practical-patterns)
- One pooler in serverless. Point the app at PgBouncer (transaction mode) or Accelerate/Neon pooling so N function instances don't exhaust DB connections. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#practical-patterns)
- Keep a parameterized raw-SQL escape hatch for the queries the ORM models awkwardly; don't fight the ORM for a window function - drop to sql\...\``. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#practical-patterns)
- Wrap the ORM in a repository module so the data layer stays swappable and call sites don't import the client everywhere. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#practical-patterns)
- Make seeds idempotent (upsert, not blind insert) so re-running is safe. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#practical-patterns)
- Pin to interactive transactions when later writes depend on earlier reads within the same atomic unit (Prisma $transaction(async tx => …)). — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#practical-patterns)

## Anti-patterns

- N+1 queries. Iterating parents and touching a lazy relation per item. Symptom: a flood of near-identical single-row SELECTs in the query log. Fix: eager-load the relation (include/with/relations/populate) or batch with DataLoader. The cardinal data-layer performance bug. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#anti-patterns)
- db push / kit push to production. No history, no rollback, silent drift. Use generated, committed migrations everywhere shared. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#anti-patterns)
- String-concatenated raw SQL. queryRaw("... " + userInput) is SQL injection - always use the parameterized tagged-template form. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#anti-patterns)
- A pool per serverless invocation with no external pooler - exhausts the DB's max_connections under concurrency. Front it with PgBouncer/Accelerate. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#anti-patterns)
- **SELECT * via the ORM when you need three columns** - fetch only what you project (select) to cut payload and avoid over-fetching. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#anti-patterns)
- Sharing one long-lived EntityManager/identity-mapped context across requests (MikroORM/TypeORM) - leaks state between users; use request-scoped contexts (RequestContext / AsyncLocalStorage). — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#anti-patterns)
- Treating Kysely or Drizzle's SQL API like an ORM - there's no relation graph or identity map; you compose SQL, you don't navigate objects. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#anti-patterns)

## Troubleshooting

- Mysterious burst of identical SELECTs → N+1; turn on query logging, eager-load or add DataLoader. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#troubleshooting)
- "too many connections" / pool timeout under load (esp. serverless) → pool sized above the DB ceiling × instance count; reduce per-instance limit and add an external transaction-mode pooler. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#troubleshooting)
- Prisma types out of date after a schema edit → re-run prisma generate (it's a generated client; the build won't pick up changes otherwise). — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#troubleshooting)
- PgBouncer transaction mode breaking prepared statements / Prisma → set pgbouncer=true on the connection string and follow the pooler-mode guidance; prepared-statement caching conflicts with transaction-pooling. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#troubleshooting)
- Kysely/Drizzle "column doesn't exist" only at runtime → the generated Database type / TS schema drifted from the DB; re-introspect / re-run kysely-codegen / drizzle-kit pull. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#troubleshooting)
- MikroORM changes not saved → you forgot em.flush(); the Unit of Work persists on flush, not on mutation. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#troubleshooting)
- Migration "drift detected" (Prisma) → the DB diverged from migration history (a manual change or a db push); resolve with migrate diff / baselining rather than another push. — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#troubleshooting)

## References

- Prisma - Prisma Client overview (generated client, type-safe queries): https://www.prisma.io/docs/orm/prisma-client — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#references)
- Prisma - Prisma Migrate (migrate dev / deploy, db push): https://www.prisma.io/docs/orm/prisma-migrate — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#references)
- Prisma - Connection pool (connection_limit, serverless, PgBouncer, v7 driver adapters): https://www.prisma.io/docs/orm/prisma-client/setup-and-configuration/databases-connections/connection-pool — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#references)
- Prisma - Relation mode (prisma vs foreignKeys): https://www.prisma.io/docs/orm/prisma-schema/data-model/relations/relation-mode — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#references)
- Prisma - Release notes / changelog (v7 Rust-free client): https://www.prisma.io/changelog — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#references)
- Drizzle ORM - Why Drizzle / overview (no codegen, no runtime engine, 1 query, serverless): https://orm.drizzle.team/docs/overview — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#references)
- Drizzle ORM - Migrations & drizzle-kit (generate/migrate/push/pull): https://orm.drizzle.team/docs/migrations ; https://orm.drizzle.team/docs/kit-overview — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#references)
- Drizzle ORM - Query data & relational queries (with): https://orm.drizzle.team/docs/data-querying ; https://orm.drizzle.team/docs/rqb-v2 — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#references)
- Kysely - Introduction (type-safe builder, not an ORM, no relations): https://kysely.dev/docs/intro — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#references)
- Kysely - Expressions (composable query building blocks) & Relations recipe: https://kysely.dev/docs/recipes/expressions ; https://kysely.dev/docs/recipes/relations — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#references)
- TypeORM - Active Record vs Data Mapper: https://typeorm.io/docs/guides/active-record-data-mapper/ — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#references)
- TypeORM - Entities & decorators; Repository: https://typeorm.io/docs/entity/entities/ ; https://typeorm.io/docs/working-with-entity-manager/working-with-repository/ — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#references)
- Sequelize - Eager loading (include) & Associations: https://sequelize.org/docs/v6/advanced-association-concepts/eager-loading/ ; https://sequelize.org/docs/v6/core-concepts/assocs/ — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#references)
- MikroORM - Unit of Work & transactions; Identity Map & request context: https://mikro-orm.io/docs/unit-of-work ; https://mikro-orm.io/docs/identity-map — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#references)
- MikroORM - Dataloaders (built-in N+1 batching): https://mikro-orm.io/docs/dataloaders — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#references)
- DataLoader - Solving the N+1 problem (GraphQL.js guide): https://www.graphql-js.org/docs/n1-dataloader/ — [source](https://llms-explorer.com/sources/mdb-context-hub/nodejs-orm-query-builders/#references)

## Where this helps

- Choosing a data-access library for a new TypeScript service — a full ORM (Prisma/TypeORM/Sequelize/MikroORM) versus a type-safe query builder (Kysely/Drizzle) versus a raw driver, based on how much of the object graph the app actually needs. — [source](https://llms-explorer.com/tree/node-js-typescript-orms-and-query-builders/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Debugging a mysterious burst of near-identical SELECT statements in a query log — the N+1 problem, fixable with eager loading or DataLoader. — [source](https://llms-explorer.com/tree/node-js-typescript-orms-and-query-builders/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Deploying to serverless or edge, where connection-pool-per-invocation behavior can exhaust a database's connection ceiling unless an external pooler like PgBouncer sits in front. — [source](https://llms-explorer.com/tree/node-js-typescript-orms-and-query-builders/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Writing a complex analytical query (window functions, recursive CTEs) that an ORM's object-graph API models awkwardly, and reaching for a query builder or a parameterized raw-SQL escape hatch instead of fighting the ORM. — [source](https://llms-explorer.com/tree/node-js-typescript-orms-and-query-builders/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*

## Project ideas

- Build a small repository-pattern wrapper around whichever ORM or builder a service uses, so call sites depend on a method like users.findActive() instead of importing the client everywhere. — [source](https://llms-explorer.com/tree/node-js-typescript-orms-and-query-builders/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Add query logging plus a DataLoader-based batching layer to a GraphQL resolver stack that's currently N+1-ing on every list-of-relations field. — [source](https://llms-explorer.com/tree/node-js-typescript-orms-and-query-builders/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Prototype the same data model in Prisma and Drizzle side by side to compare generated-client DX against SQL-first schema-as-TypeScript, using the pack's own selection criteria to decide. — [source](https://llms-explorer.com/tree/node-js-typescript-orms-and-query-builders/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Write a migration-drift checker that runs in CI and fails the build if the live schema no longer matches the committed migration history, catching a stray db push before it reaches prod. — [source](https://llms-explorer.com/tree/node-js-typescript-orms-and-query-builders/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*

## Common mistakes

- Running db push or drizzle-kit push against a shared or production environment — it leaves no migration history and no rollback path, and drifts silently from what's committed. — [source](https://llms-explorer.com/tree/node-js-typescript-orms-and-query-builders/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Building raw SQL by string concatenation instead of the parameterized tagged-template form — this is SQL injection, not a style choice. — [source](https://llms-explorer.com/tree/node-js-typescript-orms-and-query-builders/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Sharing one long-lived EntityManager or identity-mapped context (MikroORM/TypeORM) across requests instead of a request-scoped one — it leaks state between users. — [source](https://llms-explorer.com/tree/node-js-typescript-orms-and-query-builders/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Opening a fresh connection pool per serverless invocation with no external pooler in front — it exhausts the database's max_connections under concurrency long before the app code looks wrong. — [source](https://llms-explorer.com/tree/node-js-typescript-orms-and-query-builders/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*

## Known issues

- PgBouncer's transaction pooling mode conflicts with prepared-statement caching, so Prisma and similar clients need pgbouncer=true set explicitly on the connection string or queries fail unpredictably under load. — [source](https://llms-explorer.com/tree/node-js-typescript-orms-and-query-builders/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Prisma's generated client does not pick up a schema edit until prisma generate is re-run — types silently go stale otherwise. — [source](https://llms-explorer.com/tree/node-js-typescript-orms-and-query-builders/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Kysely and Drizzle's generated Database types can drift from the real schema, and the mismatch only surfaces as a runtime "column doesn't exist" error, not a compile error. — [source](https://llms-explorer.com/tree/node-js-typescript-orms-and-query-builders/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- MikroORM's Unit of Work only persists changes on em.flush() — a mutated entity that never gets flushed is silently never saved. — [source](https://llms-explorer.com/tree/node-js-typescript-orms-and-query-builders/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*

## Context files

- [Node.js & TypeScript ORMs and Query Builders](https://llms-explorer.com/downloads/sources/mdb-context-hub/nodejs-orm-query-builders.md)
