defineRelations creates soft relations at application level
The defineRelations function defines relations between tables at the application level only. Relations do not affect the database schema, do not create foreign keys implicitly, and are not database constraints. They are a higher level abstraction used purely for querying related data through Drizzle's relational query API.
one() relation configuration fields
The r.one() method accepts the following configuration fields: from (the source column for the relation), to (the target column for the relation), optional (boolean, defaults to false to make the relation required at the type level), alias (string to differentiate multiple identical relationships between two tables), and where (condition object for polymorphic relations that filters results based on specified criteria). The custom key name (e.g. 'author') is the identifier that appears in query results.
many() relation configuration fields
The r.many() method accepts the following configuration fields: from (the source column for the relation), to (the target column for the relation), alias (string to differentiate multiple identical relationships between two tables), and where (condition object for polymorphic relations that filters results based on specified criteria). The custom key name (e.g. 'feed') is the identifier that appears in query results and the relation returns an array.
One-to-one relation example with self-reference
A one-to-one self-referencing relation allows a user to invite another user. The relation uses r.one.users() with from: r.users.invitedBy and to: r.users.id, creating a reference from the invitedBy column to the id column within the same users table.
One-to-one relation with nullable type when foreign key in target table
When a one-to-one relation has the foreign key stored in the target table (like profileInfo.userId referencing users.id), the relation is nullable at the TypeScript type level. This is expressed as profileInfo: { ... } | null, indicating that a user may or may not have profile information.
One-to-many relation example: users and posts
A one-to-many relation between users and posts is defined with r.many.posts() on the users side and r.one.users() on the posts side. The relation uses from: r.posts.authorId, to: r.users.id on the one side, and from: r.users.id, to: r.posts.authorId on the many side (without explicit from/to since it can be inferred).
Many-to-many relation with through() for junction tables
Many-to-many relations use junction or join tables to store associations. The relation is defined with r.many.groups() using .through() on both the from and to columns to reference the junction table columns. Example: from: r.users.id.through(r.usersToGroups.userId), to: r.groups.id.through(r.usersToGroups.groupId). This allows selecting groups directly for each user without explicitly selecting the junction table.
defineRelationsPart separates relation definitions into multiple parts
The defineRelationsPart function allows splitting relation definitions into separate files or modules. Parts are then merged with the main relations using spread syntax when passing to the drizzle db instance: drizzle(url, { relations: { ...relations, ...part } }).
defineRelationsPart ordering rule: main relations first
When using defineRelationsPart, the main relations object must come first in the spread order: { ...relations, ...part } (correct) not { ...part, ...relations } (incorrect). This is because the main relation recursively infers all table names for autocomplete, and spreading them last would lose the relations information defined in the parts.
defineRelationsPart empty main part for autocomplete
If using only relation parts without a main relations object, one part should be empty like defineRelationsPart(schema) with no body. This ensures all tables are inferred correctly for autocomplete and complete schema information.
One-to-one indexing strategy: index foreign key in target table
For optimal performance in one-to-one relationships, create an index on the foreign key column in the target table (the table being referenced). For example, with users and profileInfo where profileInfo.userId references users.id, create an index on profileInfo.userId. This speeds up JOIN operations when querying related data.
One-to-many indexing strategy: index foreign key in many-side table
For optimal performance in one-to-many relationships, create an index on the foreign key column in the table representing the 'many' side of the relationship. For example, with users (one) and posts (many), create an index on posts.authorId. This optimizes queries that fetch a user with their posts or posts with their authors.
Many-to-many indexing strategy: three indexes on junction table
For optimal performance in many-to-many relationships, create three indexes on the junction table: (1) index on the first foreign key column individually, (2) index on the second foreign key column individually, (3) composite index on both foreign key columns together. This optimizes queries filtering by one side, the other side, or finding the connections between both entities.
alias field disambiguates multiple relations between same tables
The alias option in relation definitions allows distinguishing multiple relations between the same two tables. For example, if a posts table has both author and reviewer relations to users, each relation definition must include an alias: alias: 'author' and alias: 'reviewer' respectively. This prevents naming conflicts and allows proper differentiation.
Drizzle v1 relational queries require v2 migration
If using Relational Queries when upgrading to v1, you must upgrade to v2. This includes migrating the relations definition from v1 to v2 and migrating the queries from v1 to v2.
Relational Queries v1 removed, use defineRelations()
RQBv1 has been removed in Drizzle v1. Users must migrate to Relational Queries v2 using the new defineRelations() API. The defineRelations() function accepts a schema and a callback that returns an object mapping table names to their relationships using r.many and r.one methods.
defineRelations() syntax and examples
defineRelations() is called with two arguments: the schema and a callback function. The callback receives an object with methods r.many and r.one for defining relationships. Example: const relations = defineRelations(schema, (r) => ({ users: { posts: r.many.posts(), profile: r.one.profiles() }, posts: { author: r.one.users({ from: posts.authorId, to: users.id }) } }));
One-to-One relationship definition
In a one-to-one relationship, each record in table A is related to at most one record in table B, and each record in table B is related to at most one record in table A. It is a direct, exclusive pairing between two tables.
One-to-Many relationship definition
In a one-to-many relationship, one record in table A can be related to many records in table B, but each record in table B is related to at most one record in table A. This is a parent-child relationship pattern.
Many-to-Many relationship definition
In a many-to-many relationship, one record in table A can be related to many records in table B, and one record in table B can be related to many records in table A. This requires a junction table (also called an associative or bridging table) to link records from both tables.
Junction table for many-to-many relationships
A junction table (also called an associative table or bridging table) acts as an intermediary to link records from both tables in a many-to-many relationship. The junction table contains foreign keys to both main tables. Using a unique constraint on the composite foreign keys prevents duplicate relationships.
Many-to-many relationship with chained left joins
Chain leftJoin() calls to join multiple tables in a many-to-many pattern. The callback parameters accumulate joined tables: first call receives (sourceTable, joinedTable1), second call receives (sourceTable, joinedTable1, joinedTable2). Example: const result = await manyToManyTable.select().leftJoin(usersTable, (m2m, users) => eq(m2m.userId, users.id)).leftJoin(chatGroupsTable, (m2m, _users, chatGroups) => eq(m2m.groupId, chatGroups.id)).where((m2m, _users, userGroups) => eq(userGroups.id, 1)).execute();
Relational Query Parts with defineRelationsPart
You can separate relations config into multiple parts using defineRelationsPart helper to avoid monolithic relation definitions. Import defineRelations and defineRelationsPart from 'drizzle-orm'. Define relations with defineRelations and additional parts with defineRelationsPart, then combine them when creating the db instance: const db = drizzle(url, { relations: { ...relations, ...part } })