💡 TypeORM Cascades
Cascades
There are two types of "cascades" in TypeORM. First, we have database cascades.
Database Cascades
This is probably what you have heard of and are familiar with. It is the standard when a parent is deleted, also delete the children. These are super convenient when you have linked data and you want to automatically perform some action when a delete happens. Instead of manually deleting linked records before the deleting the target row, you can delegate this responsibility to the database through cascades. It is also worth noting that CASCADE
is not the only action that can be taken. You can also use any of the following actions:
CASCADE
- cascade the update/deleteNO ACTION
- do nothingSET NULL
- set the column tonull
SET DEFAULT
- set the column its default value
While ON DELETE
is probably the most common (and useful), there are actually two events that I normally set ON DELETE
and ON UPDATE
.
ON DELETE
is what I described above, cascade the delete event to the children, also deleting them.ON UPDATE
is when the value of the foreign key is updated - cascade the update.
I have never needed to use ON UPDATE
in practice. Presumably this implies a need to update the id
column (primary key) to an alternative value. id
columns are usually automatically generated and never need to be changed, but I always include ON UPDATE CASCADE
for the unlikely event I find myself in that situation.
In code, these cascades look something like this:
export class AuthorEntity {
@ManyToOne(() => PostEntity, (post) => post.author)
posts!: PostEntity[];
}
export class PostEntity {
@OneToMany(() => AuthorEntity, (author) => auth.posts, {
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
})
author!: AuthorEntity;
}
Note the cascade configuration is on the child relation, which in this case means when an author
is deleted, delete this post
.
TypeORM Cascades
While also referred to as a "cascade", this actually has nothing to do with database cascades. It is more of a "software cascade". Or, in other words, allows you to insert/update/delete multiple records at once in a single repository call. For example,
const author = new AuthorEntity();
author.name = 'Jason';
const post = new PostEntity();
post.name = 'TypeORM Migrations';
post.author = author;
repository.save(post);
Normally, this would not create the author
if it did not already exist. Without cascades you would need to save the author
, set it on the post
, and finally save the post
.
With cascades turned on, TypeORM will do this for you in a single call.
In code, this type of cascade looks something like this:
export class AuthorEntity {
@ManyToOne(() => PostEntity, (post) => post.author)
posts!: PostEntity[];
}
export class PostEntity {
@OneToMany(() => AuthorEntity, (author) => auth.posts, { cascade: true })
author!: AuthorEntity;
}
Of course, since these are two completely unrelated functionalities, they can be used independently.
Conclusion
I know that TypeORM cascades can be confusing, but hopefully this overview has helped clear up common misunderstandings between the two, so that they can be used more effectively in your next project.