DEV Community

Maelig
Maelig

Posted on

TypeORM - remove children with orphanedRowAction

TypeORM is a very convenient ORM for JS apps. We use it with NestJS and running it on NodeJS.

But we are not here to talk about our technical stack, but to deep dive, directly into relations with TypeORM.

When you are referencing children entities from a parent entity, you use a @OneToMany relation and a @ManyToOne relation.

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

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

  @OneToMany(
    () => ChildEntity,
    child => child.parent
  )
  children: ChildEntity[];
}

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

  @ManyToOne(
    () => ParentEntity,
    parent => parent.children,
    {nullable: false}
  )
  parent: ParentEntity;
}
Enter fullscreen mode Exit fullscreen mode

And the best part referencing children entities in the parent entity, is that you can manipulate children to add, remove or update them.
If you add {cascade: true}, every change in children will be saved when you save the parent.

Let's update the parent entity :

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

  @OneToMany(
    () => ChildEntity,
    child => child.parent,
    {cascade: true}
  )
  children: ChildEntity[];

  removeChild(childId: number): void {
    // lets remove the target from the array
    this.children = this.children.filter(child => child.id !== childId);
  }
}

export class MyService() {

  private parentEntity: ParentEntity;

  constructor(private readonly parentRepository: Repository<ParentEntity>) {}

  removeChildFromParent(childId: number) {
    this.parent.removeChild(childId);
    this.parentRepository.save(this.parent);
  }
}
Enter fullscreen mode Exit fullscreen mode

And now we are in troubles because this line this.parentRepository.save(this.parent); will throw an error, because the default behaviour when you remove an entity from the array, is to set its property parent: ParentEntity; to null and so the column in database called parent_id which is not nullable 😨😱

How the hell are we gonna do ?

Nothing, end of thread, see ya.

Come back my friends, we have a solution, and it's not well documented yet in the TypeORM doc :)

It's the magical option orphanedRowAction that will save us, but its default value that is causing our troubles : nullify.

orphanedRowAction: "nullify" | "delete" | "soft-delete" | disable - When a parent is saved (cascading enabled) without a child/children that still exists in database, this will control what shall happen to them.

All you have to do is to use it in the child entity relation to its parent (that the unclear point in TypeORM's doc to me) :

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

  @ManyToOne(
    () => ParentEntity,
      parent => parent.children,
    {
      nullable: false,
      orphanedRowAction: "delete"
    }
  )
  parent: ParentEntity;
}
Enter fullscreen mode Exit fullscreen mode

So now when you remove an entity from the parent's children array, there'll be a DELETE query to remove it from database.

And now, your mind is free.

Top comments (0)