# TypeORM Relationship

Database tables are often related to one another through relationships, also known as associations. Relationships between tables are established through foreign keys, which are columns in one table that refer to the primary key of another table. For example, a blog post may have many comments or an order could be related to the user who placed it. We have the following relation:

  • One To One (BelongsToOneRelation)
  • One To Many (HasManyRelation)
  • Many To Many (ManyToManyRelation)

# Relation Options

There are several options you can specify for relations:

  • eager: boolean - If set to true, the relation will always be loaded with the main entity when using find* methods or QueryBuilder on this entity
  • cascade: boolean | ("insert" | "update")[] - If set to true, the related object will be inserted and updated in the database. You can also specify an array of cascade options.
  • onDelete: "RESTRICT"|"CASCADE"|"SET NULL" - specifies how foreign key should behave when referenced object is deleted
  • primary: boolean - Indicates whether this relation's column will be a primary column or not.
  • nullable: boolean - Indicates whether this relation's column is nullable or not. By default it is nullable.
  • orphanedRowAction: "nullify" | "delete" | "soft-delete" - When a child row is removed from its parent, determines if the child row should be orphaned (default) or deleted (delete or soft delete).

Visit TypeORM relations (opens new window) for more info.

# One To One

One-to-one is a relation where A contains only one instance of B, and B contains only one instance of A. Let's take for example User and Profile entities. User can have only a single profile, and a single profile is owned by only a single user.

Profile Model in App/Model directory

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";

@Entity()
export class Profile {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  gender: string;

  @Column()
  photo: string;
}

User Model in App/Model directory

import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } from "typeorm";
import { Profile } from "./Profile";

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @OneToOne(() => Profile)
  @JoinColumn()
  profile: Profile;
}

Here we added @OneToOne to the profile and specify the target relation type to be Profile. We also added @JoinColumn which is required and must be set only on one side of the relation, the side that have the foreign key in the database table.

This example will produce following tables:

+-------------+--------------+----------------------------+
|                        profile                          |
+-------------+--------------+----------------------------+
| id          | int(11)      | PRIMARY KEY AUTO_INCREMENT |
| gender      | varchar(255) |                            |
| photo       | varchar(255) |                            |
+-------------+--------------+----------------------------+

+-------------+--------------+----------------------------+
|                          user                           |
+-------------+--------------+----------------------------+
| id          | int(11)      | PRIMARY KEY AUTO_INCREMENT |
| name        | varchar(255) |                            |
| profileId   | int(11)      | FOREIGN KEY                |
+-------------+--------------+----------------------------+

The side you set @JoinColumn on, is the side that contains the "relation id" and foreign keys to target entity table.

Visit TypeORM guide (opens new window) for more info.

# Many-to-one / one-to-many relations

Many-to-one / one-to-many is a relation where A contains multiple instances of B, but B contains only one instance of A. Let's take for example User and Photo entities. User can have multiple photos, but each photo is owned by only one single user.

Photo Model in App/Model directory

import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm";
import { User } from "./User";

@Entity()
export class Photo {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  url: string;

  @ManyToOne(() => User, (user) => user.photos)
  user: User;
}

User Model in App/Model directory

import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm";
import { Photo } from "./Photo";

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @OneToMany(() => Photo, (photo) => photo.user)
  photos: Photo[];
}

Here we added @OneToMany to the photos property in User Model and specified the target relation type to be Photo. You can omit @JoinColumn in a @ManyToOne or @OneToMany relation.

⚠️ @OneToMany cannot exist without @ManyToOne.

If you want to use @OneToMany, @ManyToOne is required. However, the inverse is not required: If you only care about the @ManyToOne relationship, you can define it without having @OneToMany on the related entity. Where you set @ManyToOne - its related entity will have "relation id" and foreign key.

This example will produce following tables:

+-------------+--------------+----------------------------+
|                         photo                           |
+-------------+--------------+----------------------------+
| id          | int(11)      | PRIMARY KEY AUTO_INCREMENT |
| url         | varchar(255) |                            |
| userId      | int(11)      | FOREIGN KEY                |
+-------------+--------------+----------------------------+

+-------------+--------------+----------------------------+
|                          user                           |
+-------------+--------------+----------------------------+
| id          | int(11)      | PRIMARY KEY AUTO_INCREMENT |
| name        | varchar(255) |                            |
+-------------+--------------+----------------------------+

Visit TypeORM guide (opens new window) for more info.

# Many To Many

Many-to-many is a relation where A contains multiple instances of B, and B contain 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.

Category Model in App/Model directory

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";

@Entity()
export class Category {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;
}

Question Model in App/Model directory

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({ name: "question_categories" })
  categories: Category[];
}

@JoinTable() is required for @ManyToMany relations. You must put @JoinTable on one (owning) side of relation. It also accepts a name param which will be the name of the join table.

This example will produce following tables:

+-------------+--------------+----------------------------+
|                        category                         |
+-------------+--------------+----------------------------+
| id          | int(11)      | PRIMARY KEY AUTO_INCREMENT |
| name        | varchar(255) |                            |
+-------------+--------------+----------------------------+

+-------------+--------------+----------------------------+
|                        question                         |
+-------------+--------------+----------------------------+
| id          | int(11)      | PRIMARY KEY AUTO_INCREMENT |
| title       | varchar(255) |                            |
| text        | varchar(255) |                            |
+-------------+--------------+----------------------------+

+-------------+--------------+----------------------------+
|              question_categories                        |
+-------------+--------------+----------------------------+
| questionId  | int(11)      | PRIMARY KEY FOREIGN KEY    |
| categoryId  | int(11)      | PRIMARY KEY FOREIGN KEY    |
+-------------+--------------+----------------------------+

Visit TypeORM guide (opens new window) for more info.