跳至主要内容

`query`:创建自定义 Prisma 客户端查询

信息

Prisma Client 扩展从 4.16.0 及更高版本开始正式可用。它们在 4.7.0 版本中以预览版形式引入。如果您运行的版本低于 4.16.0,请确保启用 clientExtensions 预览功能标志。

您可以使用 query Prisma 客户端扩展组件类型,来 Hook 到查询生命周期中,并修改传入的查询或其结果。

您可以使用 Prisma 客户端扩展的 query 组件来创建具有自定义行为的独立客户端。您可以将一个客户端绑定到特定筛选器或用户,而将另一个客户端绑定到另一个筛选器或用户。例如,您可以通过这种方式在行级安全 (RLS) 扩展中实现用户隔离query 扩展组件为所有自定义查询提供端到端的类型安全。

扩展 Prisma 客户端查询操作

使用 $extends 客户端级方法来创建扩展客户端。扩展客户端是标准 Prisma 客户端的变体,由一个或多个扩展包装。

使用 query 扩展组件来修改查询。您可以通过以下方式修改自定义查询:

要创建自定义查询,请使用以下结构:

const prisma = new PrismaClient().$extends({
name?: 'name',
query?: {
user: { ... } // in this case, we add a query to the `user` model
},
});

属性如下:

  • name:(可选)为扩展指定一个名称,该名称会出现在错误日志中。
  • query:定义一个自定义查询。

修改特定模型中的特定操作

query 对象可以包含映射到 Prisma 客户端操作名称的函数,例如 findUnique()findFirstfindManycountcreate。以下示例修改 user.findMany 以使用自定义查询,该查询只查找年龄大于 18 岁的用户:

const prisma = new PrismaClient().$extends({
query: {
user: {
async findMany({ model, operation, args, query }) {
// take incoming `where` and set `age`
args.where = { ...args.where, age: { gt: 18 } }

return query(args)
},
},
},
})

await prisma.user.findMany() // returns users whose age is greater than 18

在上述示例中,调用 prisma.user.findMany 会触发 query.user.findMany。每个回调都会收到一个类型安全的 { model, operation, args, query } 对象,该对象描述了查询。此对象具有以下属性:

  • model:我们想要扩展的查询所包含的模型名称。

    在上述示例中,model 是一个类型为 "User" 的字符串。

  • operation:正在扩展和执行的操作名称。

    在上述示例中,operation 是一个类型为 "findMany" 的字符串。

  • args:要扩展的特定查询输入信息。

    这是一个类型安全的对象,您可以在查询发生之前对其进行修改。您可以修改 args 中的任何属性。例外:您不能修改 includeselect,因为那样会改变预期的输出类型并破坏类型安全。

  • query:查询结果的 Promise。

    • 您可以使用 await 然后修改此 Promise 的结果,因为其值是类型安全的。TypeScript 会捕获对象上的任何不安全修改。

修改您 schema 中所有模型中的特定操作

要扩展 schema 中所有模型中的查询,请使用 $allModels 而不是特定的模型名称。例如:

const prisma = new PrismaClient().$extends({
query: {
$allModels: {
async findMany({ model, operation, args, query }) {
// set `take` and fill with the rest of `args`
args = { ...args, take: 100 }

return query(args)
},
},
},
})

修改特定模型中的所有操作

使用 $allOperations 来扩展特定模型中的所有操作。

例如,以下代码将自定义查询应用于 user 模型上的所有操作:

const prisma = new PrismaClient().$extends({
query: {
user: {
$allOperations({ model, operation, args, query }) {
/* your custom logic here */
return query(args)
},
},
},
})

修改所有 Prisma 客户端操作

使用 $allOperations 方法来修改 Prisma 客户端中存在的所有查询方法。$allOperations 既可以用于模型操作,也可以用于原始查询。

您可以按如下方式修改所有方法:

const prisma = new PrismaClient().$extends({
query: {
$allOperations({ model, operation, args, query }) {
/* your custom logic for modifying all Prisma Client operations here */
return query(args)
},
},
})

如果调用原始查询,传递给回调的 model 参数将是 undefined

例如,您可以使用 $allOperations 方法按如下方式记录查询:

const prisma = new PrismaClient().$extends({
query: {
async $allOperations({ operation, model, args, query }) {
const start = performance.now()
const result = await query(args)
const end = performance.now()
const time = end - start
console.log(
util.inspect(
{ model, operation, args, time },
{ showHidden: false, depth: null, colors: true }
)
)
return result
},
},
})

修改您 schema 中所有模型中的所有操作

使用 $allModels$allOperations 来扩展您 schema 中所有模型中的所有操作。

将自定义查询应用于您 schema 中所有模型上的所有操作:

const prisma = new PrismaClient().$extends({
query: {
$allModels: {
$allOperations({ model, operation, args, query }) {
/* your custom logic for modifying all operations on all models here */
return query(args)
},
},
},
})

修改顶级原始查询操作

要将自定义行为应用于特定的顶级原始查询操作,请使用顶级原始查询函数的名称而不是模型名称:

const prisma = new PrismaClient().$extends({
query: {
$queryRaw({ args, query, operation }) {
// handle $queryRaw operation
return query(args)
},
$executeRaw({ args, query, operation }) {
// handle $executeRaw operation
return query(args)
},
$queryRawUnsafe({ args, query, operation }) {
// handle $queryRawUnsafe operation
return query(args)
},
$executeRawUnsafe({ args, query, operation }) {
// handle $executeRawUnsafe operation
return query(args)
},
},
})

修改查询结果

您可以使用 await 然后修改 query Promise 的结果。

const prisma = new PrismaClient().$extends({
query: {
user: {
async findFirst({ model, operation, args, query }) {
const user = await query(args)

if (user.password !== undefined) {
user.password = '******'
}

return user
},
},
},
})
信息

我们包含上述示例是为了表明这是可行的。但是,出于性能原因,我们建议您使用result 组件类型来覆盖现有字段。result 组件类型在这种情况下通常能提供更好的性能,因为它只在访问时计算。query 组件类型在查询执行后计算。

将查询包装到批处理事务中

您可以将扩展查询包装到批处理事务中。例如,您可以使用此功能来实现行级安全 (RLS)。

以下示例扩展了 findFirst,使其在批处理事务中运行。

const transactionExtension = Prisma.defineExtension((prisma) => 
prisma.$extends({
query: {
user: {
// Get the input `args` and a callback to `query`
async findFirst({ args, query, operation }) {
const [result] = await prisma.$transaction([query(args)]) // wrap the query in a batch transaction, and destructure the result to return an array
return result // return the first result found in the array
},
},
},
})
)
const prisma = new PrismaClient().$extends(transactionExtension)
© . This site is unofficial and not affiliated with Prisma Data, Inc.