Implementing Soft Delete Pattern

1. Adding deletedAt Field

deletedAt DateTime?
@@index([deletedAt])
AspectDetail
NullableNull = active; date = deleted
IndexImproves filtered scans

2. Using Middleware for Soft Delete

ApproachDetail
Legacy $useIntercept delete/findMany
ExtensionUse $extends.query hooks

3. Filtering Deleted Records

const active = await prisma.user.findMany({ where: { deletedAt: null } });
StrategyDetail
Explicit filterAdd to every read
Repository wrapperEncapsulate filter in DAO
ExtensionAutomatic injection

4. Implementing Restore Functionality

await prisma.user.update({ where: { id }, data: { deletedAt: null } });
StepDetail
Reset flagSet deletedAt to null
AuditLog restoration event

5. Performing Hard Deletes

WhenDetail
GDPR erasurePermanently remove
Cleanup jobsAfter retention window

6. Using Global Soft Delete Scope

const xprisma = prisma.$extends({
  query: {
    $allModels: {
      async findMany({ args, query }) {
        args.where = { ...args.where, deletedAt: null };
        return query(args);
      }
    }
  }
});
AspectDetail
ScopeApply to all or specific models
BypassUse raw client for admin tools

7. Handling Relation Cascades

PatternDetail
Cascade soft-deleteUpdate children's deletedAt
DetachKeep children, null FK

8. Archiving Deleted Records

StrategyDetail
Archive tableMove to separate model
PartitioningPG declarative partitions
Cold storageExport to S3/Glacier

9. Implementing Paranoid Mode

ConceptDetail
Default safe readsAlways exclude deleted
Explicit opt-inNeed flag to include deleted

10. Querying Including Deleted Records

const all = await prisma.user.findMany();  // bypass extension
const onlyDeleted = await prisma.user.findMany({ where: { deletedAt: { not: null } } });
NeedApproach
All rowsUse base client without extension
Only deletedFilter deletedAt: { not: null }