Relation Filters
Top-level soft-delete filtering does not automatically change nested Prisma relations. Version 0.5 adds an opt-in traversal that injects the current soft-delete mode into to-many include and select trees.
Enable Relation Filtering
Configure the Prisma extension that handles application queries:
import { readFileSync } from 'node:fs';
import { getDMMF } from '@prisma/internals';
import { createPrismaSoftDeleteExtension } from '@nestarc/soft-delete';
const datamodel = readFileSync('prisma/schema.prisma', 'utf8');
const dmmf = await getDMMF({ datamodel });
const prisma = basePrisma.$extends(
createPrismaSoftDeleteExtension({
softDeleteModels: ['User', 'Post', 'Comment'],
relationFilters: {
enabled: true,
maxDepth: 3,
},
dmmf,
}),
);relationFilters: true is shorthand for enabling the feature with the default maximum depth of 3. DMMF metadata is required to distinguish to-many relations and resolve their target models. If it is omitted, setup throws RelationDmmfMissingError.
Prisma compatibility
The published peer range covers Prisma 5, 6, and 7. Pin @prisma/internals to the same version as prisma; see the installation guide.
Query Rewriting
With the default filter mode, this application query:
await prisma.user.findMany({
include: {
posts: {
include: { comments: true },
},
},
});is sent to Prisma with active-only filters on configured to-many soft-delete models:
{
where: { deletedAt: null },
include: {
posts: {
where: { deletedAt: null },
include: {
comments: { where: { deletedAt: null } },
},
},
},
}Existing relation where clauses are retained, while the context-controlled deletedAt predicate is enforced.
Filter Modes
| Context | Root records | To-many relations |
|---|---|---|
| Default | Active only | Active only |
@OnlyDeleted() / onlyDeleted() | Deleted only | Deleted only |
@WithDeleted() / withDeleted() | Active and deleted | Active and deleted |
@SkipSoftDelete() | No rewrite | No rewrite |
Include Deleted Rows for Selected Relations
@WithDeletedRelations() keeps normal root filtering but exempts exact relation paths:
import { Get, Param } from '@nestjs/common';
import { WithDeletedRelations } from '@nestarc/soft-delete';
@Get(':id')
@WithDeletedRelations('posts', 'posts.comments')
findOne(@Param('id') id: string) {
return this.prisma.client.user.findUnique({
where: { id: +id },
include: {
posts: {
include: { comments: true },
},
},
});
}Paths are exact dot paths from the query's root model. Exempting posts does not automatically exempt posts.comments; list both when both levels should include deleted rows.
Supported Scope
Version 0.5 supports:
- to-many relation trees under
includeandselect - nested traversal up to
maxDepth - default, only-deleted, with-deleted, and skipped contexts
- exact route-level relation-path exemptions
It does not filter to-one relations because Prisma does not accept the same nested where shape there. Nested writes are also outside the relation-filter feature.
Adoption Checklist
- Inventory queries that currently return deleted children and treat the new filtering as an intentional response-shape change.
- Enable the feature in a test environment and exercise nested
includeandselectqueries. - Add
@WithDeletedRelations()only to routes that are authorized to expose deleted child records. - Keep
maxDepthbounded and measure complex nested queries against production-like data. - Verify DMMF availability during application startup.
See Decorators for the other request-level modes and v0.5 Changes & Fixes for upgrade guidance.