or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

data-source.mdentity-definition.mdentity-schema.mdevents.mdfind-options.mdindex.mdmigrations.mdquery-builder.mdrelationships.mdrepository.md

relationships.mddocs/

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

```