The declarative soft-delete solution for MikroORM.
npm i mikro-orm-soft-delete
Inspired by: mikro-orm/mikro-orm#1492 (comment)
Library Version | ORM Version |
---|---|
v1.x.x | v6.x.x |
v0.x.x | v5.x.x |
- It is now mandatory to register
SoftDeleteHandler
as an extension for this library to work. See Initialization for details. - Base entities no longer accept any generic type parameters.
To enable soft-delete for your MikroORM
instance, register SoftDeleteHandler
as an extension in the initialization config:
import { SoftDeleteHandler } from "mikro-orm-soft-delete";
await MikroORM.init({
// ...
extensions: [SoftDeleteHandler],
// ...
});
Put a SoftDeletable
decorator on your entity definition to make it soft-deletable:
import { SoftDeletable } from "mikro-orm-soft-delete";
@SoftDeletable(() => User, "deletedAt", () => new Date())
@Entity()
export class User extends BaseEntity {
@PrimaryKey()
id: number;
@Property({ nullable: true })
deletedAt?: Date;
}
The above code snippet means that:
- A filter with conditions
{ deletedAt: null }
is applied toUser
and enabled by default, so that those soft-deleted entities will be excluded from your queries. This filter could be disabled by:import { SOFT_DELETABLE_FILTER } from "mikro-orm-soft-delete"; repo.find({ ... }, { filters: { [SOFT_DELETABLE_FILTER]: false } }); repo.find({ ... }, { filters: false }); // if you are sure that there are no other filters enabled
- When an deletion command is executed on a
User
entity, itsdeletedAt
field will be set to a newly instantiatedDate
. You could find alldelete
statements replaced withupdate
ones under MikroORM's debugging mode:repo.remove(user); await repo.flush(); user.id !== undefined; // true user.deletedAt instanceof Date; // true
cascade: [Cascade.Remove]
andorphanRemoval: true
still work withrepo.remove()
. But you would have to avoid removing items from collections when usingorphanRemoval
as it's currently not possible to intercept deletions caused by these operations.
Aside from passing the parameters by positions, there is also an object-based API that accepts a config object instead:
@SoftDeletable({
type: () => User,
field: 'deletedAt',
value: () => new Date(),
})
By default, a null
value is used in the filter to exclude soft-deleted entities: { deletedAt: null }
. However, if the default value of the field is not null
, the query would not work as we expected.
For example, when the field is isDeleted
and the default value is false
, the query { isDeleted: null }
would not match any entities.
In this case, an additional option valueInitial
needs to be specified:
@SoftDeletable({
type: () => User,
field: 'isDeleted',
value: () => true,
valueInitial: false, // indicating that the default value of `isDeleted` is `false`.
})
...which would make the query look like { isDeleted: false }
to find all the entities that is not soft-deleted.
This option could also be specified through the 4th argument:
@SoftDeletable(() => User, 'isDeleted', () => true, false)
Inheritance is supported for the SoftDeletable
decorator, thus it is possible to create a SoftDeletableBaseEntity
to make all the sub entity classes soft-deletable:
@SoftDeletable(() => SoftDeletableBaseEntity, "deletedAt", () => new Date())
export abstract class SoftDeletableBaseEntity extends BaseEntity {
@Property({ nullable: true })
deletedAt?: Date;
}
Currently it's impossible to hard-delete an entity marked as soft-deletable. As a workaround, the native API could be used for hard-deletions:
em.nativeDelete(...);