Getting Started
Database Drivers
Schema Integrations
Seed Generators
Utilities and Testing
The following files were used as context for generating this wiki page:
Entity Relations form the core of the relational query engine in Drizzle ORM. They act as the abstraction layer that maps TypeScript schema definitions to database-level operations, enabling complex graph-like queries across multiple tables while maintaining type safety. By decoupling the query definition from the underlying SQL dialect, the subsystem allows developers to express deeply nested relationships without manually writing verbose JOIN or aggregate logic.
The architecture solves the "N+1" problem by translating high-level relational configuration—such as One and Many definitions—into optimized, dialect-specific SQL. It uses an internal configuration model to understand table structures, primary keys, and foreign key constraints, which it then uses to dynamically generate subqueries, lateral joins, or aggregate functions as required by the backend dialect (Postgres, MySQL, or Gel).
The design prioritizes flexibility and performance by performing a multi-pass transformation during query building. It handles complex cases like cyclic references and lateral subqueries by normalizing relations before dispatching them to the query builder. This design enables seamless interaction with the query builders, where relations are resolved into SQL execution plans at runtime.
The subsystem relies on a relational config structure to understand how entities connect. This structure, typically extracted via extractTablesRelationalConfig, maps database table objects into a graph of One and Many relations. This metadata allows the dialect to determine, for example, if a Many relation necessitates a JSON_AGG or similar aggregation to satisfy a nested result set.
Sources: drizzle-kit/src/introspect-pg.ts:210-210, drizzle-orm/src/relations.ts
Normalization is the process of flattening and standardizing relation definitions. The normalizeRelation utility function transforms raw metadata into a predictable format (with fields and references arrays), ensuring the query builder doesn't need to handle dialect-specific implementation details of a relationship during the building phase. This provides a uniform contract for joining tables regardless of their origin.
Sources: drizzle-orm/src/pg-core/dialect.ts:1223-1223, drizzle-orm/src/gel-core/dialect.ts:1292-1292
The core engine responsible for turning relational config into SQL resides in methods like buildRelationalQueryWithoutPK or buildRelationalQuery. These functions act as a recursive traversal engine. When a developer executes a relational query, the builder walks the relationship graph, dynamically constructing aliases and LEFT JOIN operations.
Many relation is encountered, the builder may inject subqueries or lateral joins to maintain row integrity while aggregating related records.Sources: drizzle-orm/src/pg-core/dialect.ts:1150-1444, drizzle-orm/src/mysql-core/dialect.ts:658-1336
When the builder decides how to fetch related data, it evaluates the nature of the relation. Many relations (which imply 1:N or M:N) are typically treated with subquery aggregation, whereas One relations may be joined directly. A key architectural guard exists here: the code explicitly distinguishes between queries with primary keys and those without, selecting different strategies to ensure the database can resolve rows uniquely.
Tip
When building relational queries, isLateralJoin logic determines if a subquery needs to be correlated with the outer query. This is a critical performance path for handling complex joins.
Sources: drizzle-orm/src/pg-core/dialect.ts:858-859, drizzle-orm/src/mysql-core/dialect.ts:1208-1212
The dialect-specific implementation files provide the final SQL generation logic. Because PostgreSQL, MySQL, and Gel handle JSON aggregation differently (e.g., json_agg vs json_arrayagg), the dialect methods buildRelationalQueryWithoutPK or buildRelationalQuery encapsulate these differences. This isolates the relational engine from the database driver, allowing the same high-level query syntax to function across different database backends.
Sources: drizzle-orm/src/pg-core/dialect.ts:1369-1371, drizzle-orm/src/mysql-core/dialect.ts:1249-1250
The subsystem also provides utilities for reconstructing these relationships from existing databases. The introspect-pg module scans schema definitions, foreign key constraints, and table names to output TypeScript code that defines the relations() configuration. This bridges the gap between raw database structure and the relational engine’s requirements.
Sources: drizzle-kit/src/introspect-pg.ts:210-272
Sources: drizzle-orm/src/pg-core/dialect.ts:1150-1444, drizzle-orm/src/mysql-core/dialect.ts:658-1336