Implementing Soft Deletes with Prisma in Next.js API Routes

User avatar placeholder
Written by Tamzid Ahmed

June 1, 2026

Soft deletes are a critical pattern for data safety in modern applications. Implementing soft deletes with Prisma in Next.js API routes ensures you never lose data accidentally while maintaining query efficiency. This guide provides a step-by-step implementation for safe data management.

What is a Soft Delete?

A soft delete (or logical delete) marks records as deleted without removing them from the database. Instead of permanently deleting data, we set a flag (like deletedAt) to indicate the record’s status. This approach preserves data integrity and allows for recovery.

Why Use Soft Deletes in Your Next.js Application?

Soft deletes offer several advantages for production applications. They prevent accidental data loss, enable audit trails, and allow for easy restoration. However, they also introduce complexity: you must consistently filter deleted records in queries and manage storage growth. Use soft deletes when data recovery is critical, such as in financial or compliance-driven systems.

  • Pros: Data recovery, audit history, avoids foreign key constraint issues
  • Cons: Query complexity, potential performance impact if not indexed, storage overhead

Prisma Soft Delete Implementation in Next.js API Routes

Implementing soft deletes requires two key steps: adding a deletedAt field to your model and creating middleware to filter deleted records. Let’s walk through both.

Step 1: Update Your Prisma Schema

Add a deletedAt field to your Prisma schema. This field will store the timestamp when a record is soft-deleted. Here’s how to define it:

model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String?
  deletedAt DateTime? @default(null)
}

Then, run prisma migrate dev to update your database. The deletedAt field is nullable by default, but you can set a default value of null to indicate active records.

Step 2: Configure Prisma Middleware

Prisma middleware allows us to automatically filter out soft-deleted records in every query. This ensures we never accidentally show deleted data.

Create a middleware function in your Prisma client setup (e.g., in lib/prisma.ts):

import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

prisma.$use(async (params, next) => {
  if (params.action === 'findMany' || params.action === 'findFirst') {
    // Add where condition to exclude deleted records
    params.args.where = {
      ...params.args.where,
      deletedAt: { equals: null }
    }
  }
  return next(params)
})

export default prisma

This middleware automatically appends a deletedAt: null condition to all findMany and findFirst queries. For updates and deletes, we’ll handle them separately in API routes.

Handling Soft Deletes in Queries

When you need to include deleted records (e.g., for an admin dashboard), you can bypass the middleware by using prisma.$transaction or by creating a separate Prisma client instance without middleware. However, the middleware approach is generally preferred for consistency.

For queries that require deleted records, explicitly include the condition:

const users = await prisma.user.findMany({
  where: {
    deletedAt: { not: null }
  }
})

Remember: In your middleware, we only added the filter for findMany and findFirst. For other operations (like update, delete), we don’t automatically filter. So when restoring, we must update the record without the middleware filter (which is fine because the middleware doesn’t affect updates).

Trade-offs and Best Practices

Soft deletes are powerful but require discipline. Here are key best practices:

  • Index the deletedAt column for faster queries.
  • Audit log changes to track who deleted what and when.
  • Use a scheduled job to permanently delete records after a retention period (e.g., 30 days).
  • Test thoroughly to ensure no queries accidentally show deleted records.

Conclusion

Implementing soft deletes with Prisma in Next.js API routes enhances data safety and flexibility. By adding a deletedAt field, using middleware for automatic filtering, and carefully handling delete/restore operations, you can prevent accidental data loss while maintaining query performance. Always consider the trade-offs and apply best practices to keep your application robust. Start by adding soft deletes to critical models in your next project.

Leave a Comment