Many-to-many is a relation where A contains multiple instances of B, and B contains multiple instances of A. Let's take for example Question and Category entities. A question can have multiple categories, and each category can have multiple questions.
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"
@Entity()
export class Category {
@PrimaryGeneratedColumn()
id: number
@Column()
name: string
}
import {
Entity,
PrimaryGeneratedColumn,
Column,
ManyToMany,
JoinTable,
} from "typeorm"
import { Category } from "./Category"
@Entity()
export class Question {
@PrimaryGeneratedColumn()
id: number
@Column()
title: string
@Column()
text: string
@ManyToMany(() => Category)
@JoinTable()
categories: Category[]
}
@JoinTable() is required for @ManyToMany relations. You must put @JoinTable on one (owning) side of relation.
In this example we did not call save or softRemove for category1 and category2, but they will be automatically saved and soft-deleted when the cascade of relation options is set to true like this:
import {
Entity,
PrimaryGeneratedColumn,
Column,
ManyToMany,
JoinTable,
} from "typeorm"
import { Category } from "./Category"
@Entity()
export class Question {
@PrimaryGeneratedColumn()
id: number
@ManyToMany(() => Category, (category) => category.questions, {
cascade: true,
})
@JoinTable()
categories: Category[]
}
Loading many-to-many relations
To load questions with categories inside you must specify the relation in FindOptions:
With eager loading enabled on a relation, you don't have to specify relations in the find command as it will ALWAYS be loaded automatically. If you use QueryBuilder eager relations are disabled, you have to use leftJoinAndSelect to load the relation.
Bi-directional relations
Relations can be uni-directional and bi-directional. Uni-directional relations are relations with a relation decorator only on one side. Bi-directional relations are relations with decorators on both sides of a relation.
We just created a uni-directional relation. Let's make it bi-directional:
import { Entity, PrimaryGeneratedColumn, Column, ManyToMany } from "typeorm"
import { Question } from "./Question"
@Entity()
export class Category {
@PrimaryGeneratedColumn()
id: number
@Column()
name: string
@ManyToMany(() => Question, (question) => question.categories)
questions: Question[]
}
import {
Entity,
PrimaryGeneratedColumn,
Column,
ManyToMany,
JoinTable,
} from "typeorm"
import { Category } from "./Category"
@Entity()
export class Question {
@PrimaryGeneratedColumn()
id: number
@Column()
title: string
@Column()
text: string
@ManyToMany(() => Category, (category) => category.questions)
@JoinTable()
categories: Category[]
}
We just made our relation bi-directional. Note that the inverse relation does not have a @JoinTable. @JoinTable must be only on one side of the relation.
Bi-directional relations allow you to join relations from both sides using QueryBuilder:
In case you need to have additional properties in your many-to-many relationship, you have to create a new entity yourself. For example, if you would like entities Question and Category to have a many-to-many relationship with an additional order column, then you need to create an entity QuestionToCategory with two ManyToOne relations pointing in both directions and with custom columns in it:
import { Entity, Column, ManyToOne, PrimaryGeneratedColumn } from "typeorm"
import { Question } from "./question"
import { Category } from "./category"
@Entity()
export class QuestionToCategory {
@PrimaryGeneratedColumn()
public questionToCategoryId: number
@Column()
public questionId: number
@Column()
public categoryId: number
@Column()
public order: number
@ManyToOne(() => Question, (question) => question.questionToCategories)
public question: Question
@ManyToOne(() => Category, (category) => category.questionToCategories)
public category: Category
}
Additionally you will have to add a relationship like the following to Question and Category: