Prisma Middleware — Intercept and Modify Database Operations
In this tutorial, you will learn about Prisma Middleware. We cover key concepts, practical examples, and best practices to help you master this topic.
Prisma middleware lets you intercept and modify database operations before they execute or after they complete, enabling cross-cutting concerns like logging, soft deletes, Caching, and access control.
What You'll Learn
By the end of this lesson you will add middleware to intercept query operations, modify query parameters and results, implement soft deletes, add audit logging, and enforce access control at the middleware level.
Why It Matters
Middleware allows you to implement cross-cutting concerns without modifying every Repository or service. A single middleware can enforce soft deletes, log slow queries, or check permissions across the entire application.
Real-World Use
DodaZIP uses middleware for soft deletes (intercepting delete operations and setting deletedAt instead) and for audit logging (recording all create/update/delete operations with user context).
Creating Middleware
Add middleware to the Prisma Client.
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
// Simple logging middleware
prisma.$use(async (params, next) => {
const before = Date.now();
// Execute the original operation
const result = await next(params);
const after = Date.now();
const duration = after - before;
console.log(`Query: ${params.model}.${params.action}`);
console.log(`Duration: ${duration}ms`);
return result;
});
# middleware_setup.py
# Middleware structure
def middleware_structure():
print("Middleware Structure:")
print()
print("prisma.$use(async (params, next) => {")
print(" // Before: params contains operation details")
print(" // - params.model: model name (e.g., 'User')")
print(" // - params.action: action name (e.g., 'create')")
print(" // - params.args: the query arguments")
print(" // - params.dataPath: data path for nested queries")
print()
print(" const result = await next(params)")
print()
print(" // After: result contains the query result")
print()
print(" return result")
print("})")
print()
print("Multiple middleware executes in order:")
print(" Order: first registered = first to run")
middleware_structure()
Intercepting Operations
Modify query parameters before execution.
// Soft delete middleware
prisma.$use(async (params, next) => {
// Intercept delete and deleteMany
if (params.action === 'delete') {
// Change to update with deletedAt
params.action = 'update';
params.args.data = { deletedAt: new Date() };
}
if (params.action === 'deleteMany') {
params.action = 'updateMany';
if (params.args.data) {
params.args.data.deletedAt = new Date();
} else {
params.args.data = { deletedAt: new Date() };
}
}
// Intercept findMany/findFirst to exclude soft-deleted
if (params.action === 'findMany' || params.action === 'findFirst') {
if (!params.args) params.args = {};
if (!params.args.where) params.args.where = {};
params.args.where.deletedAt = null;
}
return next(params);
});
// Usage: prisma.user.delete({ where: { id: 1 } })
// Actually performs: prisma.user.update({
// where: { id: 1 },
// data: { deletedAt: new Date() }
// })
# soft_delete.py
# Soft delete middleware pattern
def soft_delete_pattern():
print("Soft Delete Middleware Pattern:")
print()
print("Intercepted actions:")
print(" delete -> update with deletedAt")
print(" deleteMany -> updateMany with deletedAt")
print(" findMany -> add where: { deletedAt: null }")
print(" findFirst -> add where: { deletedAt: null }")
print()
print("Params modification:")
print(" params.action = 'update'")
print(" params.args.data = { deletedAt: new Date() }")
print()
print("Benefits:")
print(" - Transparent to application code")
print(" - No changes needed in services/repos")
print(" - Can be enabled/disabled per model")
soft_delete_pattern()
Audit Logging Middleware
Record all data changes for Compliance.
// Audit logging middleware
prisma.$use(async (params, next) => {
const result = await next(params);
// Log mutations (create, update, delete)
const mutationActions = ['create', 'update', 'delete', 'updateMany', 'deleteMany', 'createMany'];
if (mutationActions.includes(params.action)) {
const auditEntry = {
model: params.model,
action: params.action,
timestamp: new Date().toISOString(),
args: JSON.stringify(params.args),
userId: getCurrentUserId(), // From context
};
// Store audit log in database or external service
await prisma.auditLog.create({ data: auditEntry });
}
return result;
});
# audit_logging.py
# Audit logging middleware
def audit_middleware():
print("Audit Logging Middleware:")
print()
print("Records for every mutation:")
print(" - model: 'User'")
print(" - action: 'create'")
print(" - args: { data: { ... } }")
print(" - userId: 'user_123'")
print(" - timestamp: '2026-06-28T...'")
print()
print("Use cases:")
print(" - Compliance requirements (SOC 2, HIPAA)")
print(" - Debugging data changes")
print(" - Security investigations")
print(" - User activity history")
audit_middleware()
Access Control Middleware
Enforce permissions at the query level.
// Access control middleware
prisma.$use(async (params, next) => {
const user = getCurrentUser();
// Only admins can delete
if (params.action === 'delete' || params.action === 'deleteMany') {
if (!user || user.role !== 'ADMIN') {
throw new Error('Only admins can delete records');
}
}
// Automatically filter by organization
if (user) {
const orgScopedModels = ['File', 'Post', 'Project'];
if (orgScopedModels.includes(params.model)) {
if (!params.args) params.args = {};
if (!params.args.where) params.args.where = {};
params.args.where.organizationId = user.organizationId;
}
}
return next(params);
});
# access_control.py
# Access control middleware patterns
def access_control():
print("Access Control Middleware Patterns:")
print()
print("1. Role-based filtering:")
print(" - Admin: full access")
print(" - User: own data only")
print(" - Viewer: read only")
print()
print("2. Organization scoping:")
print(" - Automatically add org filter")
print(" - Prevents cross-org data leaks")
print()
print("3. Field-level masking:")
print(" - Hide sensitive fields from certain roles")
print(" - Filter params.select to remove fields")
print()
print("4. Rate limiting:")
print(" - Throttle expensive operations")
access_control()
Common Mistakes
Not calling next(params): Middleware must call next(params) to continue the chain. Without it, the query never executes.
Modifying params incorrectly: Directly modifying params works, but be careful with nested objects. Create deep copies when needed.
Performance impact of middleware: Each middleware adds overhead. Keep middleware lightweight and avoid database calls inside middleware.
Middleware ordering issues: Multiple middleware run in registration order. The first registered runs first. Plan middleware ordering carefully.
Not handling async errors: Middleware that throws an error prevents the query from executing. Use try/catch for expected failures.
Practice Questions
What is Prisma middleware used for? Intercepting and modifying database operations for cross-cutting concerns like logging, soft deletes, and access control.
How do you register middleware? Call prisma.$use(callback) before any queries. Multiple middleware can be registered.
How do you soft delete using middleware? Intercept delete/deleteMany actions, change them to update/updateMany with deletedAt timestamp.
What should every middleware return? It must return the result of
next(params)to continue the chain.Challenge: Create middleware that implements soft deletes, audit logging for mutations, and automatic organization scoping for all queries.
FAQ
Mini Project
Create a middleware stack for a production application: soft delete middleware, audit logging middleware, organization scoping middleware, and slow query logging middleware.
def middleware_stack():
print("Production Middleware Stack:")
print()
print("Order matters (first registered = outer):")
print()
print("1. Organization Scoping (outermost)")
print(" - Adds organizationId filter for all queries")
print()
print("2. Audit Logging")
print(" - Logs all mutations to audit_log table")
print()
print("3. Soft Delete")
print(" - Intercepts delete -> update with deletedAt")
print(" - Filters out soft-deleted records from reads")
print()
print("4. Slow Query Logging (innermost)")
print(" - Logs queries taking longer than 100ms")
middleware_stack()
What's Next
Next: Prisma Errors for error handling.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro