Prisma Soft Delete Middleware
prisma-soft-delete-middleware is a utility library for Prisma that provides an efficient way to implement soft deletion for database records using Prisma's middleware system. The current stable version is `1.3.1`. It is actively maintained with a history of regular updates, including recent bug fixes and feature enhancements, though its release cadence may slow as the community shifts towards Prisma Extensions. This library's key differentiator is its ability to automatically handle cascading soft deletes across related models and to filter out soft-deleted records from various Prisma queries, including `findFirst`, `findMany`, `findUnique`, and operations involving `include` or `select`. It achieves this by intelligently modifying query arguments before they reach the database, leveraging `prisma-nested-middleware` for complex nested scenarios. While functional and maintained, Prisma's middleware system is deprecated in favor of Prisma Extensions, and users are encouraged to migrate to `prisma-extension-soft-delete` for future-proof applications.
Common errors
-
TypeError: Cannot read properties of undefined (reading 'length') at applyMiddleware
cause The middleware was not correctly applied to the Prisma Client using `$use()`, or the `models` configuration is empty/incorrect.fixEnsure `prisma.$use(createSoftDeleteMiddleware({ models: { /* your models */ } }))` is called before any database operations. -
Unknown arg `includeSoftDeleted` in `where` for type `QueryUserArgs`.
cause `includeSoftDeleted` is a special parameter added by the middleware, not a standard Prisma query argument, and should not be placed inside a `where` clause.fixPass `includeSoftDeleted: true` directly as a top-level argument to the query function (e.g., `prisma.user.findMany({ includeSoftDeleted: true })`) as enabled by the middleware. -
Error: Property 'deleted' does not exist on type 'User'.
cause A model configured for soft deletion in the middleware does not have a `deleted` field (or the custom field name specified) in its Prisma schema.fixAdd the `deleted: Boolean @default(false)` field to the respective model in your `schema.prisma` file, then run `npx prisma generate`. -
Error: Middleware not invoked for model 'Post'
cause The `Post` model (or any other model) was not included in the `models` configuration object passed to `createSoftDeleteMiddleware`.fixEnsure all models you wish to apply soft delete to are explicitly listed in the `createSoftDeleteMiddleware` configuration: `{ models: { User: true, Post: true } }`.
Warnings
- deprecated Prisma's native middleware system, which this library uses, is officially deprecated. While this library will continue to be maintained, the Prisma team recommends migrating to Prisma Extensions for future development. The library's author provides `prisma-extension-soft-delete` as a direct migration path.
- gotcha The middleware requires a specific 'deleted' field (defaulting to `deleted: Boolean @default(false)`) on any model configured for soft deletion. Failing to define this field in your Prisma schema will lead to runtime errors when the middleware attempts to modify queries.
- gotcha Version `1.2.0` introduced support for Prisma v5's `find*OrThrow` actions. If you are using Prisma v5 and `find*OrThrow` methods, older versions of this middleware may not function correctly with those specific actions.
- gotcha As of `v1.3.0`, the middleware supports `deleted` fields that store 'truthy' values (e.g., a timestamp) instead of strictly boolean `true/false`. If your `deleted` field is not a boolean, ensure `deleteValue` in the middleware configuration is set correctly for your schema.
Install
-
npm install prisma-soft-delete-middleware -
yarn add prisma-soft-delete-middleware -
pnpm add prisma-soft-delete-middleware
Imports
- createSoftDeleteMiddleware
const createSoftDeleteMiddleware = require('prisma-soft-delete-middleware')import { createSoftDeleteMiddleware } from 'prisma-soft-delete-middleware' - SoftDeleteMiddleware
import type { SoftDeleteMiddleware } from 'prisma-soft-delete-middleware'
Quickstart
import { PrismaClient } from '@prisma/client';
import { createSoftDeleteMiddleware } from 'prisma-soft-delete-middleware';
// Example Prisma Schema:
// model User {
// id String @id @default(uuid())
// email String @unique
// name String?
// deleted Boolean @default(false)
// posts Post[]
// }
// model Post {
// id String @id @default(uuid())
// title String
// content String?
// published Boolean @default(false)
// authorId String
// author User @relation(fields: [authorId], references: [id])
// deleted Boolean @default(false)
// }
const prisma = new PrismaClient();
prisma.$use(createSoftDeleteMiddleware({
models: {
User: true, // Enable soft delete for User model
Post: { field: 'deleted', deleteValue: true, includeSoftDeleted: false },
},
}));
async function main() {
// Create a user and a post
const user = await prisma.user.create({
data: { email: 'test@example.com', name: 'Test User' },
});
console.log('Created user:', user);
const post = await prisma.post.create({
data: { title: 'First Post', authorId: user.id },
});
console.log('Created post:', post);
// Soft delete the user (and cascade to post if configured)
await prisma.user.delete({ where: { id: user.id } });
console.log(`Soft deleted user with ID: ${user.id}`);
// Attempt to find the user (should not be found by default)
const foundUser = await prisma.user.findUnique({ where: { id: user.id } });
console.log('Found user after soft delete (default):', foundUser);
// Find the user, explicitly including soft-deleted records
const foundSoftDeletedUser = await prisma.user.findUnique({
where: { id: user.id },
// This is a special flag added by the middleware config for the model
includeSoftDeleted: true
});
console.log('Found soft-deleted user (explicitly):', foundSoftDeletedUser);
// Find all posts (soft-deleted post should not be found by default)
const allPosts = await prisma.post.findMany();
console.log('All posts (default):', allPosts);
// Find all posts, including soft-deleted ones (if 'Post' model was configured with includeSoftDeleted in middleware)
const allPostsIncludingDeleted = await prisma.post.findMany({ includeSoftDeleted: true });
console.log('All posts (including deleted):', allPostsIncludingDeleted);
}
main().catch(e => {
console.error(e);
process.exit(1);
}).finally(async () => {
await prisma.$disconnect();
});