Middlewares act as query-level lifecycle hooks, which allow you to perform an action before or after a query runs. Use the prisma.$use method to add middlewares:

const prisma = new PrismaClient()
// Middleware 1
prisma.$use(async (params, next) => {
// Manipulate params here
const result = await next(params)
// See results here
return result
})
// Middleware 2
prisma.$use(async (params, next) => {
// Manipulate params here
const result = await next(params)
// See results here
return result
})
// Queries here

params represent parameters available in the middleware, such as the name of the query, and next represents the next middleware in the stack or the original Prisma Client query.

Possible use cases for middleware include:

There are many more use cases for middleware - this list serves as inspiration for the types of problems that middleware is designed to address.

Examples

Here are some examples that show how to user middlewares in practice:

Where to add middleware

Add Prisma middleware outside the context of the request handler, otherwise each request adds a new instance of the middleware to the stack. The following example demonstrates where to add Prisma middleware in the context of an Express app:

import express from 'express'
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
prisma.$use(async (params, next) => {
// Manipulate params here
const result = await next(params)
// See results here
return result
})
const app = express()
app.get('/feed', async (req, res) => {
// NO MIDDLEWARE HERE
const posts = await prisma.post.findMany({
where: { published: true },
include: { author: true },
})
res.json(posts)
})

Running order and the middleware stack

If you have multiple middlewares, the running order for each separate query is:

  1. All logic before await next(params) in each middleware, in descending order
  2. All logic after await next(params) in each middleware, in ascending order

Depending on where you are in the stack, await next(params) either:

  • Runs the next middleware (in middlewares #1 and #2 in the example) or
  • Runs the original Prisma Client query (in middleware #3)
const prisma = new PrismaClient()
// Middleware 1
prisma.$use(async (params, next) => {
console.log(params.args.data.title)
console.log('1')
const result = await next(params)
console.log('6')
return result
})
// Middleware 2
prisma.$use(async (params, next) => {
console.log('2')
const result = await next(params)
console.log('5')
return result
})
// Middleware 3
prisma.$use(async (params, next) => {
console.log('3')
const result = await next(params)
console.log('4')
return result
})
const create = await prisma.post.create({
data: {
title: 'Welcome to Prisma Day 2020',
},
})
const create2 = await prisma.post.create({
data: {
title: 'How to Prisma!',
},
})

Output:

Welcome to Prisma Day 2020
1
2
3
4
5
6
How to Prisma!
1
2
3
4
5
6

Performance and appropriate use cases

Middleware executes for every query, which means that overuse has the potential to negatively impact performance. To avoid adding performance overheads:

  • Check the params.model and params.action properties early in your middleware to avoid running logic unnecessarily:

    prisma.$use(async (params, next) => {
    if (params.model == 'Post' && params.action == 'delete') {
    // Logic only runs for delete action and Post model
    }
    return next(params)
    })
  • Consider whether middleware is the appropriate solution for your scenario. For example:

    • If you need to populate a field, can you use the @default attribute?
    • If you need to set the value of a DateTime field, can you use the now() function or the @updatedAt attribute?
    • If you need to perform more complex validation, can you use a CHECK constraint in the database itself?
Edit this page on GitHub