中间件
已弃用:中间件在 4.16.0 版本中已弃用。
我们建议使用 Prisma Client 扩展的 query
组件类型 作为中间件的替代方案。Prisma Client 扩展于 4.7.0 版本首次作为预览特性引入,并在 4.16.0 版本中正式发布 (GA)。
Prisma Client 扩展允许你创建独立的 Prisma Client 实例,并将每个客户端绑定到特定的过滤器或用户。例如,你可以将客户端绑定到特定用户以提供用户隔离。Prisma Client 扩展还提供端到端类型安全。
中间件充当查询级别的生命周期钩子,允许你在查询运行之前或之后执行操作。使用 prisma.$use
方法添加中间件,如下所示
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
在使用 批量事务 时,不要在中间件中多次调用 next
。这会导致你脱离事务并产生意外结果。
params
表示中间件中可用的参数,例如查询名称;而 next
表示 堆栈中的下一个中间件或原始的 Prisma Client 查询。
中间件的可能用例包括
- 设置或覆盖字段值 - 例如,设置博客评论的上下文语言
- 验证输入数据 - 例如,通过外部服务检查用户输入是否存在不当语言
- 拦截
delete
查询并将其更改为update
,以执行 软删除 - 记录执行查询所需的时间
中间件还有许多其他用例 - 此列表旨在为中间件旨在解决的问题类型提供灵感。
示例
以下示例场景展示了如何在实践中使用中间件
中间件示例:软删除
以下示例使用中间件执行软删除。软删除意味着通过将 deleted 等字段更改为 true 来标记记录已删除,而不是实际从数据库中移除记录。使用软删除的原因包括
中间件示例:日志记录
以下示例记录了 Prisma Client 查询运行所需的时间
中间件示例:会话数据
以下示例将每个 Post 的 language 字段设置为上下文语言(例如,从会话状态中获取)
在哪里添加中间件
将 Prisma Client 中间件添加到请求处理程序上下文之外,否则每个请求都会向堆栈添加一个新的中间件实例。以下示例演示了在 Express 应用程序上下文中添加 Prisma Client 中间件的位置
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)
})
运行顺序和中间件堆栈
如果你有多个中间件,每个单独查询的运行顺序是
- 每个中间件中
await next(params)
之前的所有逻辑,按降序排列 - 每个中间件中
await next(params)
之后的所有逻辑,按升序排列
根据你在堆栈中的位置,await next(params)
要么
- 运行下一个中间件(示例中的中间件 #1 和 #2 中)或
- 运行原始的 Prisma Client 查询(在中间件 #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!',
},
})
输出
Welcome to Prisma Day 2020
1
2
3
4
5
6
How to Prisma!
1
2
3
4
5
6
性能和适当的用例
中间件对每个查询都会执行,这意味着过度使用可能会对性能产生负面影响。为避免增加性能开销
-
在中间件的早期检查
params.model
和params.action
属性,以避免不必要地运行逻辑prisma.$use(async (params, next) => {
if (params.model == 'Post' && params.action == 'delete') {
// Logic only runs for delete action and Post model
}
return next(params)
}) -
考虑中间件是否适合你的场景。例如
- 如果你需要填充字段,能否使用
@default
属性? - 如果你需要设置
DateTime
字段的值,能否使用now()
函数或@updatedAt
属性? - 如果你需要执行更复杂的验证,能否在数据库本身中使用
CHECK
约束?
- 如果你需要填充字段,能否使用