API with NestJS #65. Implementing soft deletes using MikroORM and filters

NestJS SQL

This entry is part 65 of 121 in the API with NestJS

In this article, we look into the filters feature and implement soft deletes. With them, we can mark an entity as deleted without removing it from the database permanently.

You can get the code from this article in this repository.

Introducing MikroORM filters

We can provide various filters when querying data using functions such as and . For example, let’s query only short posts using the LENGTH function built into PostgreSQL. To do that, we also need to create a smart query condition.

posts.service.ts

Above, we use the helper to bypass a strict type check in the interface built into MikroORM. We need it, because there is no column named .

Instead of the above approach, we can use the decorator to predefine filter criteria and attach them to a class.

post.entity.ts

The property works in a similar way to queries in functions such as and .

Thanks to attaching our filter, we can now use it in our queries.

posts.service.ts

Adding arguments

Besides simple boolean-based filters, we can pass additional arguments through an object. Let’s make our previous filter more generic by adding the option of specifying the maximum content length.

Thanks to the above, we can now pass the content length when querying posts.

posts.service.ts

Enabling filters by default

So far, we’ve been creating filters we can use through functions such as and . Instead of that, we can define filters toggled by default. For example, let’s create a filter that skips posts that have an empty string for content.

Even though our filter works by default, we can turn it off for a particular query if we need to.

Implementing soft deletes

The idea behind soft deletes is that instead of permanently removing records from the database, we only mark them as deleted. Thanks to doing so, we can always restore the data deleted previously. In addition, it can help us increase the user experience of our application by creating an undo button, for example.

If you want to know more about the identity column above, check out Serial type versus identity columns in PostgreSQL and TypeORM

If we want to mark a post as deleted, we need to change to true.

A disadvantage of soft deletes is that we always need to consider the column when performing various other queries.

Fortunately, MikroORM will be able to do this for us. Still, it takes some additional computing power to filter out deleted entities.

There is also an important catch with columns marked as unique. Let’s imagine the following scenario:

  • create a table where every title needs to be unique,
  • insert a post with a given title,
  • delete the above post with a soft delete,
  • try to insert a post with the same title again.

Doing the above would result in an error, unfortunately.

Soft deletes with MikroORM

To use MikroORM to achieve soft deletes, we can create a custom decorator with a filter. While on it, we can create a column with the deletion date instead of a boolean flag.

If you want to know more about handling dates with PostgrteSQL, check out Managing date and time with PostgreSQL and TypeORM

withSoftDelete.ts

Thanks to writing above, we filter out all posts marked as deleted.

post.entity.ts

Please notice that we use the decorator above on the property. Thanks to doing that, we improve the performance of various queries. Since many of our queries will include checking if a post is deleted, creating an index might be a good idea.

If you want to know more about indexes, check out API with NestJS #14. Improving performance of our Postgres database with indexes

We also need to add a new method to our service that sets the value of the column.

posts.service.ts

Fetching deleted posts

We can modify our filter to accept arguments if we need to fetch the deleted posts or all posts.

withSoftDelete.ts

We need to pass an additional argument to the filter to get a deleted post.

posts.service.ts

Restoring deleted posts

The method we’ve created above can come in handy when restoring a deleted post. We need to set the column back to null to do that.

posts.service.ts

Summary

In this article, we’ve gone through the feature of filters in MikroORM. While we might prefer to write the query-related logic in a service, creating reusable filters might be a useful pattern. Besides that, we’ve learned about what soft delete is and what are its advantages and disadvantages. We’ve also created a reusable filter that helps us implement soft deletes with MikroORM.

Series Navigation<< API with NestJS #64. Transactions with PostgreSQL and MikroORMAPI with NestJS #66. Improving PostgreSQL performance with indexes using MikroORM >>
Subscribe
Notify of
guest
1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
bryan-gc
bryan-gc
1 year ago

You could add a global filter in the module configuration to get the same behavior of filtering all soft deleted rows. I think the getOnlyDeleted filter could be added too. Anyway, this approach or the one from the post doesn’t work with pivot tables.

For example this

where userRefreshTokens is a relation of @OneToMany would execute this query

I need to add the filter manually to get the same behavior with relationships