I am currently working on one feature that should implement Many-To-Many relationship with custom properties using NestJS and TypeORM.
Tech used
- NestJS
- PostgreSQL
- TypeORM
Tables info
Skill Table
| id | Name |
|---|---|
| 1 | .NET |
| 2 | Azure |
| 3 | JS |
Resume Table
| id | Name | slug |
|---|---|---|
| 1 | resume-1 | resume-1 |
| 2 | resume-2 | resume-2 |
| 3 | resume-3 | resume-3 |
Pivot Table
| id | resumeId | skillId | level |
|---|---|---|---|
| 1 | 1 | 1 | Advanced |
| 2 | 1 | 2 | Advanced |
| 3 | 2 | 3 | Advanced |
Expected result
- Return all resumes even if no skills are present
- Filter query if search params are passed
The challenge
The second point from expected results is pretty much clear. But, the first point is very difficult to handle with TypeORM.
Since it is a Many-To-Many one would expect to start with the Pivot Table and right join the two tables. That will result in getting all of the records and setting NULL for the records (skill columns) that have no skills.
Something like this:
SELECT * FROM pivot_table pt
RIGHT JOIN skill s ON s.id = pt.skillId
RIGHT JOIN resume r ON r.id = pt.resumeId
Result
| id | resumeId | skillId | level | s_id | s_name | r_id |
|---|---|---|---|---|---|---|
| 1 | 1 | 1 | Advanced | 1 | .NET | 1 |
| 2 | 1 | 2 | Advanced | 2 | Azure | 1 |
| 3 | 2 | 3 | Advanced | 3 | JS | 2 |
| 4 | 3 | null | null | null | null | null |
TypeORM
TypeORM does not support right join
Valid TypeORM argument - Github issue
Solution
SELECT * FROM (SELECT * FROM "resume") R1
LEFT JOIN (SELECT * FROM "resume_to_skill") R2 ON (R1.ID = R2."resumeId")
LEFT JOIN (SELECT * FROM "skill") S1 ON (S1.ID = R2."skillId")
Models
Resume.entity.ts
@Entity()
@Unique(['slug'])
export class Resume {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
slug: string;
@OneToMany(() => ResumeToSkill, (resumeToSkill) => resumeToSkill.resume)
resumeToSkill!: ResumeToSkill[];
}
Skill.entity.ts
@Entity()
export class Skill {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@OneToMany(() => ResumeToSkill, (resumeToSkill) => resumeToSkill.skill)
resumeToSkill!: ResumeToSkill[];
}
ResumeToSkill.entity.ts
@Entity()
@Unique(['resume', 'skill'])
export class ResumeToSkill {
@PrimaryGeneratedColumn()
id: number;
@ManyToOne(() => Resume, (resume) => resume.resumeToSkill, {
onDelete: 'CASCADE',
})
resume: number;
@ManyToOne(() => Skill, (skill) => skill.resumeToSkill, {
onDelete: 'CASCADE',
})
skill: string;
@Column()
level: string;
}
Request
I tried researching on how to create such a query using TypeORM, but no result. I struggle to figure out how to start with the "resume" table and then left join the subqueries. I would really appreciate it If you could point me to the right direction or help me resolve this.

SELECT * FROM "resume" R1 LEFT JOIN "resume_to_skill" R2 ON R1.ID = R2."resumeId" LEFT JOIN "skill" S1 ON S1.ID = R2."skillId".