How to retrieve a nested relationship using TypeORM

You have a TypeORM entity. It has a relationship. The relationship entity has a nested relationship to a third. And you need that third entity to save the world and crack the bank vault open. How the hell do we get it?

How to retrieve a nested relationship using TypeORM
Photo by Tobias Fischer / Unsplash

Given a OneToMany relationship to an entity containing a ManyToOne relationship, how do we retrieve the nested relationship's entity when making a MySQL query using TypeORM?

Let's say we have chairs, which are assigned to users via chairs assignments, and users. This would be our main entity's model:

import { Column, Entity, JoinColumn, ManyToOne, OneToMany, PrimaryColumn } from 'typeorm';

import { ChairsInterface } from './chairs.entity';
import { ChairsAssignmentsSchema } from './chairs_assignments.schema';

const CHAIRS_TABLE = 'chairs';

@Entity(CHAIRS_TABLE)
export class ChairsSchema implements ChairsInterface {
  @PrimaryColumn({
    nullable: false,
    type: Number,
  })
  id_chair: number;

  @OneToMany((type) => ChairsAssignmentsSchema, (assignment) => assignment.chair)
  @JoinColumn({ referencedColumnName: 'id_chair' })
  assignments: ChairsAssignmentsSchema[];
}

This, our first relationship's entity's schema:

import { Column, Entity, JoinColumn, ManyToOne, PrimaryColumn } from 'typeorm';

import { ChairssSchema } from './chairs.schema';
import { ChairsAssignmentsInterface } from './chairs_assignments.entity';
import { UsersSchema } from './users.schema';

const CHAIRS_ASSIGNMENTS_TABLE = 'chairs_assignments';

@Entity(CHAIRS_ASSIGNMENTS_TABLE)
export class ChairssAssignmentsSchema implements ChairsAssignmentsInterface {
  @PrimaryColumn({
    nullable: false,
    type: Number,
  })
  id_assignment: number;

  @Column({
    nullable: false,
    type: Number,
  })
  id_user: number;

  @ManyToOne(() => ChairsSchema, (tool) => tool.affectations)
  @JoinColumn({ name: 'id_chair' })
  chair: ChairsSchema;

  @ManyToOne(() => ChairsAssignmentsSchema, (assignment) => assignment.user)
  @JoinColumn({ name: 'id_user' })
  user: UsersSchema;
}

And that, our nested relationship's entity's schema:

import { Column, Entity, JoinColumn, ManyToOne, PrimaryColumn } from 'typeorm';

import { UsersInterface } from './users.entity';
import { UsersServicesSchema } from './users_service.schema';
import { UsersTypesSchema } from './users_type.schema';

const USERS_TABLE = 'users';

@Entity(USERS_TABLE)
export class UsersSchema implements UsersInterface {
  @Column({
    nullable: false,
    type: String,
  })
  email_user: string;

  @PrimaryColumn({
    nullable: false,
    type: Number,
  })
  id_user: number;

  @Column({
    nullable: false,
    type: String,
  })
  name_user: string;
}

Then, we first need to get the assignments, like this

    return this.repository
      .createQueryBuilder('chair')
      .leftJoinAndMapMany(
        'chair.assignments',
        ChairsAssignmentsSchema,
        'chairAssignment',
        'chair.id_chair=chairAssignment.id_chair',
      )
      .getMany();

And then each assignment's user, like that

    return this.repository
      .createQueryBuilder('chair')
      .leftJoinAndMapMany(
        'chair.assignments',
        ChairsAssignmentsSchema,
        'chairAssignment',
        'chair.id_chair=chairAssignment.id_chair',
      )
      .leftJoinAndMapOne(
        'chairAssignment.user',
        UsersSchema,
        'user',
        'chairAssignment.id_user=user.id_user',
      )
      .getMany();

And there you go! We have all our chairs, with every single one of their assignments, and each assignment's user!