@nestbolt/soft-delete
Introduction
Soft delete for NestJS TypeORM entities -- mark rows as deleted instead of removing them, with restore, force delete, and trashed-query helpers.
@nestbolt/soft-delete adds soft-delete semantics to NestJS TypeORM entities. Decorate any entity with @SoftDeletable() and a deletedAt column, then call softDelete(), restore(), or forceDelete() through a typed service or directly on the entity instance via the mixin.
Key Features
- Non-destructive by default --
softDelete()writesdeletedAt = now()instead of issuing aDELETE. Rows stay queryable. - Restore and force delete --
restore()clearsdeletedAt,forceDelete()issues a realDELETEfor when you actually need the row gone. - Per-entity column override -- default
deleted_atcolumn is configurable globally viaforRoot()or per entity via@SoftDeletable({ columnName }). - Query helpers --
withTrashed()returns aSelectQueryBuilderthat includes deleted rows;onlyTrashed()returns one that selects only deleted rows. - Entity mixin --
SoftDeletableMixinaddssoftDelete(),restore(),forceDelete(),isDeleted(),isTrashed(), andgetDeletedAt()directly on entity instances. repo.remove()interception -- a TypeORM subscriber best-effort interceptsrepository.remove()calls and turns them into soft deletes for decorated entities.- Event system -- emits
soft-delete.deleted,soft-delete.restored, andsoft-delete.force-deletedvia@nestjs/event-emitter(optional).
Architecture
The package is built around several core components:
SoftDeleteModule registers all providers globally. You call SoftDeleteModule.forRoot() or SoftDeleteModule.forRootAsync() once in your root module, and SoftDeleteService becomes available everywhere via dependency injection.
SoftDeleteService is the primary API surface. It provides softDelete(), restore(), forceDelete(), withTrashed(), onlyTrashed(), and isSoftDeletable(). It also resolves the deleted-at column name from module options + entity-level overrides, and emits events when the optional @nestjs/event-emitter is registered.
@SoftDeletable() decorator marks an entity class as soft-deletable and optionally overrides the column name for that entity.
SoftDeletableMixin is an optional mixin that adds convenience methods directly to your entity instances so you can call post.softDelete() instead of service.softDelete(Post, post.id).
SoftDeleteSubscriber registers itself on the TypeORM DataSource and intercepts repository.remove() calls -- if the entity is decorated and not already soft-deleted, it sets the deleted-at column instead of letting the row be physically removed.
Basic Usage
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";
import { SoftDeletable, SoftDeletableMixin } from "@nestbolt/soft-delete";
@SoftDeletable()
@Entity("posts")
export class Post extends SoftDeletableMixin(class { id!: string }) {
@PrimaryGeneratedColumn("uuid")
declare id: string;
@Column()
title!: string;
@Column({ name: "deleted_at", type: "datetime", nullable: true, default: null })
deletedAt!: Date | null;
}
// Soft delete, restore, force delete
await post.softDelete();
await post.restore();
await post.forceDelete();
// Inspect state
post.isDeleted(); // true after softDelete()
post.getDeletedAt(); // Date | nullNext Steps
- Installation -- install the package and its peer dependencies.
- Quick Start -- set up the module, decorate an entity, and soft-delete your first record in minutes.
- Configuration --
forRoot,forRootAsync, and the fullSoftDeleteServiceAPI. - Decorator --
@SoftDeletable()and@IncludeDeleted()options reference. - Mixin -- add
softDelete(),restore(),forceDelete(), and friends directly to your entities. - Subscriber -- how
repository.remove()is intercepted and when to prefer the explicit API. - Events -- listen to
soft-delete.deleted,soft-delete.restored, andsoft-delete.force-deleted.