Search code examples
typeorm

TypeOrm - Custom Many to Many not pulling relation data


because I don't want use cascade to update the join table and I want custom columns, I've created a custom many to many relationship. However when I query the relation it only provides the values in the join table and doesn't pull the relation data.

User

@Entity('user')
export class User {
  @PrimaryColumn()
  id: string;

  @OneToMany(
    () => UserArtistFollowing,
    (userArtistFollowing) => userArtistFollowing.user
  )
  following: UserArtistFollowing[];
}

Artist


@Entity('artist')
export class Artist {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @OneToMany(
    () => UserArtistFollowing,
    (userArtistFollowing) => userArtistFollowing.artist
  )
  usersFollowing: UserArtistFollowing[];

}

UserArtistFollowing


@Entity('userArtistFollowing')
export class UserArtistFollowing {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column()
  userId!: string;

  @Column()
  artistId!: string;

  @ManyToOne(
    () => User,
    (user) => user.following
  )
  user!: User;

  @ManyToOne(
    () => Artist,
    (artist) => artist.usersFollowing
  )
  artist!: Artist;

  @CreateDateColumn()
  createdAt!: Date;

  @UpdateDateColumn()
  updatedAt!: Date;
}

Query

    const user = await getManager()
      .getRepository(Models.User)
      .findOne({
        where: { id: id },
        relations: ['following'],
      });

So that query only provides the id, userId, artistId, but not the Artist array of objects (which is the data I need).

Thoughts?


Solution

  • After further testing, turns out you can use find -> relations with these kinds of custom many to many relationships. You just have to specify the relation in dotted notation for nested relations.

          const user = await getManager()
            .getRepository(Models.User)
            .findOne({
              where: { id },
              relations: [
    // you have to specify the join table first (following) to retrieve the columns in the join table
                'following',
    // then you use dotted notation to get the relation from the join table
                'following.artist',
    // another example of a deeply nested relation
                'favourites',
                'favourites.song',
                'favourites.song.supportingArtists',
                'favourites.song.supportingArtists.artist',
              ],
            });
    
    

    You can also use join with a nested leftJoinAndSelect, but its more tedious.

        const user = await getManager()
          .getRepository(Models.User)
          .findOne({
            where: { id },
            join: {
              alias: 'user',
              leftJoinAndSelect: {
                following: 'user.following',
                artists: 'following.artist',
              },
            },
          });
    

    Here is the updated entities

    UserArtistFollowing

    @Entity('userArtistFollowing')
    export class UserArtistFollowing {
      @PrimaryColumn()
      userId: string;
    
      @PrimaryColumn()
      artistId: string;
    
      @ManyToOne(
        () => User,
        (user) => user.following
      )
      user!: User;
    
      @ManyToOne(
        () => Artist,
        (artist) => artist.usersFollowing
      )
      artist!: Artist;
    
      @CreateDateColumn()
      createdAt!: Date;
    
      @UpdateDateColumn()
      updatedAt!: Date;
    }
    
    

    Artist

    @Entity('artist')
    export class Artist {
      @PrimaryGeneratedColumn('uuid')
      id: string;
    
      @OneToMany(
        () => UserArtistFollowing,
        (userArtistFollowing) => userArtistFollowing.artist
      )
      usersFollowing: UserArtistFollowing[];
    }
    
    

    User

    @Entity('user')
    export class User {
      @PrimaryColumn()
      id: string;
    
      @OneToMany(
        () => UserArtistFollowing,
        (userArtistFollowing) => userArtistFollowing.user
      )
      following: UserArtistFollowing[];
    }