0
# Relationship Management
1
2
Powerful relationship definition system supporting one-to-one, one-to-many, many-to-one, and many-to-many associations with advanced configuration options for modeling complex data relationships between entities.
3
4
## Capabilities
5
6
### Relationship Decorators
7
8
Core decorators for defining associations between entities with automatic foreign key management and lazy/eager loading support.
9
10
```typescript { .api }
11
/**
12
* Defines a one-to-one relationship between entities
13
* @param typeFunctionOrTarget - Target entity class or string name
14
* @param inverseSide - Property name on the target entity (optional)
15
* @param options - Relationship configuration options
16
*/
17
function OneToOne<T>(
18
typeFunctionOrTarget: string | ((type?: any) => ObjectType<T>),
19
inverseSide?: string | ((object: T) => any),
20
options?: RelationOptions
21
): PropertyDecorator;
22
23
/**
24
* Defines a one-to-many relationship (parent side of the relationship)
25
* @param typeFunctionOrTarget - Target entity class or string name
26
* @param inverseSide - Property name on the target entity that points back
27
* @param options - Relationship configuration options
28
*/
29
function OneToMany<T>(
30
typeFunctionOrTarget: string | ((type?: any) => ObjectType<T>),
31
inverseSide: string | ((object: T) => any),
32
options?: RelationOptions
33
): PropertyDecorator;
34
35
/**
36
* Defines a many-to-one relationship (child side of the relationship)
37
* @param typeFunctionOrTarget - Target entity class or string name
38
* @param inverseSide - Property name on the target entity (optional)
39
* @param options - Relationship configuration options
40
*/
41
function ManyToOne<T>(
42
typeFunctionOrTarget: string | ((type?: any) => ObjectType<T>),
43
inverseSide?: string | ((object: T) => any),
44
options?: RelationOptions
45
): PropertyDecorator;
46
47
/**
48
* Defines a many-to-many relationship between entities
49
* @param typeFunctionOrTarget - Target entity class or string name
50
* @param inverseSide - Property name on the target entity (optional)
51
* @param options - Relationship configuration options
52
*/
53
function ManyToMany<T>(
54
typeFunctionOrTarget: string | ((type?: any) => ObjectType<T>),
55
inverseSide?: string | ((object: T) => any),
56
options?: RelationOptions
57
): PropertyDecorator;
58
59
interface RelationOptions {
60
/** Whether to load relation eagerly */
61
eager?: boolean;
62
/** Whether this relation is cascading */
63
cascade?: boolean | ("insert" | "update" | "remove" | "soft-remove" | "recover")[];
64
/** Custom foreign key column name */
65
foreignKeyConstraintName?: string;
66
/** Whether to create foreign key constraint */
67
createForeignKeyConstraints?: boolean;
68
/** Relation persistence/update options */
69
persistence?: boolean;
70
/** Whether relation can be null */
71
nullable?: boolean;
72
/** Default ordering for relations */
73
orderBy?: Record<string, "ASC" | "DESC">;
74
/** Relation comment */
75
comment?: string;
76
}
77
```
78
79
**Usage Examples:**
80
81
```typescript
82
import { Entity, Column, PrimaryGeneratedColumn, OneToOne, OneToMany, ManyToOne, ManyToMany } from "typeorm";
83
84
@Entity()
85
export class User {
86
@PrimaryGeneratedColumn()
87
id: number;
88
89
@Column()
90
name: string;
91
92
// One-to-One: User has one Profile
93
@OneToOne(() => Profile, profile => profile.user, {
94
cascade: true,
95
eager: true
96
})
97
profile: Profile;
98
99
// One-to-Many: User has many Posts
100
@OneToMany(() => Post, post => post.author, {
101
cascade: ["insert", "update"],
102
orderBy: { createdAt: "DESC" }
103
})
104
posts: Post[];
105
106
// Many-to-Many: User belongs to many Categories
107
@ManyToMany(() => Category, category => category.users)
108
categories: Category[];
109
}
110
111
@Entity()
112
export class Profile {
113
@PrimaryGeneratedColumn()
114
id: number;
115
116
@Column()
117
bio: string;
118
119
// Inverse side of One-to-One
120
@OneToOne(() => User, user => user.profile)
121
user: User;
122
}
123
124
@Entity()
125
export class Post {
126
@PrimaryGeneratedColumn()
127
id: number;
128
129
@Column()
130
title: string;
131
132
// Many-to-One: Many Posts belong to one User
133
@ManyToOne(() => User, user => user.posts, {
134
nullable: false,
135
onDelete: "CASCADE"
136
})
137
author: User;
138
139
// Many-to-Many: Post belongs to many Tags
140
@ManyToMany(() => Tag, tag => tag.posts, {
141
cascade: true
142
})
143
tags: Tag[];
144
}
145
```
146
147
### Join Configuration
148
149
Decorators for configuring how relationships are stored and queried in the database.
150
151
```typescript { .api }
152
/**
153
* Configures the join column for relationships
154
* @param options - Join column configuration options
155
*/
156
function JoinColumn(options?: JoinColumnOptions | JoinColumnOptions[]): PropertyDecorator;
157
158
/**
159
* Configures the join table for many-to-many relationships
160
* @param options - Join table configuration options
161
*/
162
function JoinTable(options?: JoinTableOptions): PropertyDecorator;
163
164
interface JoinColumnOptions {
165
/** Name of the foreign key column */
166
name?: string;
167
/** Name of the referenced column */
168
referencedColumnName?: string;
169
/** Whether foreign key column allows NULL */
170
foreignKeyConstraintName?: string;
171
}
172
173
interface JoinTableOptions {
174
/** Name of the join table */
175
name?: string;
176
/** Join column for the owning side */
177
joinColumn?: JoinColumnOptions;
178
/** Join column for the inverse side */
179
inverseJoinColumn?: JoinColumnOptions;
180
/** Database where join table is created */
181
database?: string;
182
/** Schema where join table is created */
183
schema?: string;
184
}
185
```
186
187
**Usage Examples:**
188
189
```typescript
190
import { Entity, ManyToOne, OneToOne, ManyToMany, JoinColumn, JoinTable } from "typeorm";
191
192
@Entity()
193
export class User {
194
@PrimaryGeneratedColumn()
195
id: number;
196
197
// One-to-One with custom join column
198
@OneToOne(() => Profile)
199
@JoinColumn({
200
name: "profile_id",
201
referencedColumnName: "id",
202
foreignKeyConstraintName: "FK_user_profile"
203
})
204
profile: Profile;
205
206
// Many-to-Many with custom join table
207
@ManyToMany(() => Role)
208
@JoinTable({
209
name: "user_roles",
210
joinColumn: { name: "user_id", referencedColumnName: "id" },
211
inverseJoinColumn: { name: "role_id", referencedColumnName: "id" }
212
})
213
roles: Role[];
214
}
215
216
@Entity()
217
export class Post {
218
@PrimaryGeneratedColumn()
219
id: number;
220
221
// Many-to-One with custom foreign key column name
222
@ManyToOne(() => User)
223
@JoinColumn({ name: "author_id" })
224
author: User;
225
226
@ManyToOne(() => Category)
227
@JoinColumn([
228
{ name: "category_id", referencedColumnName: "id" },
229
{ name: "category_type", referencedColumnName: "type" }
230
])
231
category: Category;
232
}
233
```
234
235
### Relation Loading and IDs
236
237
Utilities for loading relationship data and accessing foreign key IDs without loading the entire related entity.
238
239
```typescript { .api }
240
/**
241
* Exposes the ID of a relation without loading the relation
242
* @param relation - Name of the relation property
243
* @param options - Relation ID options
244
*/
245
function RelationId<T>(
246
relation: string | ((object: T) => any),
247
alias?: string,
248
queryBuilderFactory?: (qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>
249
): PropertyDecorator;
250
251
/**
252
* Exposes the count of a relation without loading the relation
253
* @param relation - Name of the relation property
254
* @param alias - Alias for the count property
255
* @param queryBuilderFactory - Optional query builder for filtering
256
*/
257
function RelationCount<T>(
258
relation: string | ((object: T) => any),
259
alias?: string,
260
queryBuilderFactory?: (qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>
261
): PropertyDecorator;
262
```
263
264
**Usage Examples:**
265
266
```typescript
267
import { Entity, OneToMany, ManyToOne, RelationId, RelationCount } from "typeorm";
268
269
@Entity()
270
export class User {
271
@PrimaryGeneratedColumn()
272
id: number;
273
274
@Column()
275
name: string;
276
277
@OneToMany(() => Post, post => post.author)
278
posts: Post[];
279
280
// Get post count without loading posts
281
@RelationCount((user: User) => user.posts)
282
postCount: number;
283
284
// Get published post count with query builder
285
@RelationCount((user: User) => user.posts, "publishedPostCount", qb =>
286
qb.andWhere("post.published = :published", { published: true })
287
)
288
publishedPostCount: number;
289
}
290
291
@Entity()
292
export class Post {
293
@PrimaryGeneratedColumn()
294
id: number;
295
296
@Column()
297
title: string;
298
299
@ManyToOne(() => User, user => user.posts)
300
author: User;
301
302
// Get author ID without loading author
303
@RelationId((post: Post) => post.author)
304
authorId: number;
305
306
@ManyToOne(() => Category)
307
category: Category;
308
309
@RelationId((post: Post) => post.category)
310
categoryId: number;
311
}
312
313
// Usage in queries
314
const posts = await postRepository.find({
315
select: ["id", "title", "authorId", "categoryId", "publishedAt"]
316
// No need to join or load author/category relations
317
});
318
```
319
320
### Cascade Operations
321
322
Configure automatic operations on related entities when parent entities are modified.
323
324
```typescript { .api }
325
type CascadeOption = boolean | ("insert" | "update" | "remove" | "soft-remove" | "recover")[];
326
327
// Cascade examples in relation options
328
interface RelationOptions {
329
cascade?: CascadeOption;
330
}
331
```
332
333
**Cascade Behavior Examples:**
334
335
```typescript
336
@Entity()
337
export class User {
338
@OneToMany(() => Post, post => post.author, {
339
cascade: true // All operations cascade
340
})
341
posts: Post[];
342
343
@OneToMany(() => Comment, comment => comment.author, {
344
cascade: ["insert", "update"] // Only insert and update cascade
345
})
346
comments: Comment[];
347
348
@OneToOne(() => Profile, profile => profile.user, {
349
cascade: ["insert", "remove"] // Insert and remove cascade
350
})
351
profile: Profile;
352
}
353
354
// Usage - cascading will automatically save/remove related entities
355
const user = new User();
356
user.name = "John";
357
user.posts = [
358
{ title: "Post 1", content: "Content 1" },
359
{ title: "Post 2", content: "Content 2" }
360
];
361
362
// Saves user AND posts due to cascade
363
await userRepository.save(user);
364
365
// Removes user AND posts due to cascade
366
await userRepository.remove(user);
367
```
368
369
### Tree Relationships
370
371
Special decorators for hierarchical/tree-like data structures.
372
373
```typescript { .api }
374
/**
375
* Marks entity as a tree structure
376
* @param type - Tree type: "adjacency-list", "closure-table", "materialized-path"
377
*/
378
function Tree(type: "adjacency-list" | "closure-table" | "materialized-path"): ClassDecorator;
379
380
/**
381
* Defines the parent relation in a tree structure
382
*/
383
function TreeParent(): PropertyDecorator;
384
385
/**
386
* Defines the children relation in a tree structure
387
*/
388
function TreeChildren(): PropertyDecorator;
389
390
/**
391
* Defines a column that stores the level/depth in the tree
392
*/
393
function TreeLevelColumn(): PropertyDecorator;
394
```
395
396
**Tree Structure Examples:**
397
398
```typescript
399
import { Entity, Tree, TreeParent, TreeChildren, TreeLevelColumn } from "typeorm";
400
401
@Entity()
402
@Tree("adjacency-list")
403
export class Category {
404
@PrimaryGeneratedColumn()
405
id: number;
406
407
@Column()
408
name: string;
409
410
@TreeParent()
411
parent: Category;
412
413
@TreeChildren()
414
children: Category[];
415
416
@TreeLevelColumn()
417
level: number;
418
}
419
420
// Usage with TreeRepository
421
const treeRepository = dataSource.getTreeRepository(Category);
422
423
// Find all root categories
424
const roots = await treeRepository.findRoots();
425
426
// Find all descendants of a category
427
const descendants = await treeRepository.findDescendants(category);
428
429
// Find category ancestors
430
const ancestors = await treeRepository.findAncestors(category);
431
432
// Get the complete tree
433
const tree = await treeRepository.findTrees();
434
```
435
436
## Relationship Patterns
437
438
### Loading Strategies
439
440
- **Lazy Loading** (default): Relations are loaded on-demand when accessed
441
- **Eager Loading**: Relations are automatically loaded with the entity
442
- **Explicit Loading**: Relations are loaded using repository methods or query builder
443
444
### Foreign Key Constraints
445
446
TypeORM automatically creates foreign key constraints based on relationship definitions. You can customize constraint names and behavior:
447
448
```typescript
449
@ManyToOne(() => User, {
450
nullable: false,
451
onDelete: "CASCADE", // Database-level cascade
452
onUpdate: "CASCADE"
453
})
454
author: User;
455
```
456
457
### Bidirectional Relationships
458
459
Always define both sides of bidirectional relationships for consistency:
460
461
```typescript
462
// Parent side
463
@OneToMany(() => Post, post => post.author)
464
posts: Post[];
465
466
// Child side
467
@ManyToOne(() => User, user => user.posts)
468
author: User;
469
```