`query`:创建自定义 Prisma 客户端查询
Prisma 客户端扩展从版本 4.16.0 及更高版本开始正式可用。它们是在 4.7.0 版本的预览版中引入的。如果你运行的版本早于 4.16.0,请确保启用 clientExtensions
Preview 功能标志。
¥Prisma Client extensions are Generally Available from versions 4.16.0 and later. They were introduced in Preview in version 4.7.0. Make sure you enable the clientExtensions
Preview feature flag if you are running on a version earlier than 4.16.0.
你可以使用 query
Prisma 客户端扩展 组件类型来钩子查询生命周期并修改传入查询或其结果。
¥You can use the query
Prisma Client extensions component type to hook into the query life-cycle and modify an incoming query or its result.
你可以使用 Prisma 客户端扩展 query
组件来创建独立客户端。这提供了 middlewares 的替代方案。你可以将一个客户端绑定到特定过滤器或用户,并将另一个客户端绑定到另一个过滤器或用户。例如,你可以执行此操作以在行级安全性 (RLS) 扩展中获取 用户隔离。此外,与中间件不同,query
扩展组件为你提供端到端类型安全性。了解有关 query
扩展与中间件的更多信息。
¥You can use Prisma Client extensions query
component to create independent clients. This provides an alternative to middlewares. You can bind one client to a specific filter or user, and another client to another filter or user. For example, you might do this to get user isolation in a row-level security (RLS) extension. In addition, unlike middlewares the query
extension component gives you end-to-end type safety. Learn more about query
extensions versus middlewares.
扩展 Prisma 客户端查询操作
¥Extend Prisma Client query operations
使用 $extends
客户级方法 创建 扩展客户端。扩展客户端是标准 Prisma 客户端的一种变体,由一个或多个扩展封装。
¥Use the $extends
client-level method to create an extended client. An extended client is a variant of the standard Prisma Client that is wrapped by one or more extensions.
使用 query
扩展组件来修改查询。你可以通过以下方式修改自定义查询:
¥Use the query
extension component to modify queries. You can modify a custom query in the following:
要创建自定义查询,请使用以下结构:
¥To create a custom query, use the following structure:
const prisma = new PrismaClient().$extends({
name?: 'name',
query?: {
user: { ... } // in this case, we add a query to the `user` model
},
});
属性如下:
¥The properties are as follows:
-
name
:(可选)指定错误日志中显示的扩展名。¥
name
: (optional) specifies a name for the extension that appears in error logs. -
query
:定义自定义查询。¥
query
: defines a custom query.
修改特定模型中的特定操作
¥Modify a specific operation in a specific model
query
对象可以包含映射到 Prisma 客户端操作 名称的函数,例如 findUnique()
、findFirst
、findMany
、count
和 create
。以下示例将 user.findMany
修改为使用自定义查询,仅查找 18 岁以上的用户:
¥The query
object can contain functions that map to the names of the Prisma Client operations, such as findUnique()
, findFirst
, findMany
, count
, and create
. The following example modifies user.findMany
to a use a customized query that finds only users who are older than 18 years:
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 }
对象。该对象具有以下属性:
¥In the above example, a call to prisma.user.findMany
triggers query.user.findMany
. Each callback receives a type-safe { model, operation, args, query }
object that describes the query. This object has the following properties:
-
model
:我们要扩展的查询的包含模型的名称。¥
model
: the name of the containing model for the query that we want to extend.在上面的示例中,
model
是"User"
类型的字符串。¥In the above example, the
model
is a string of type"User"
. -
operation
:正在扩展和执行的操作的名称。¥
operation
: the name of the operation being extended and executed.在上面的示例中,
operation
是"findMany"
类型的字符串。¥In the above example, the
operation
is a string of type"findMany"
. -
args
:具体查询输入信息待扩展。¥
args
: the specific query input information to be extended.这是一个类型安全的对象,你可以在查询发生之前对其进行更改。你可以改变
args
中的任何属性。例外:你不能改变include
或select
,因为这会改变预期的输出类型并破坏类型安全性。¥This is a type-safe object that you can mutate before the query happens. You can mutate any of the properties in
args
. Exception: you cannot mutateinclude
orselect
because that would change the expected output type and break type safety. -
query
:对查询结果的 promise。¥
query
: a promise for the result of the query.-
你可以使用
await
,然后更改此 Promise 的结果,因为它的值是类型安全的。TypeScript 捕获对象上的任何不安全突变。¥You can use
await
and then mutate the result of this promise, because its value is type-safe. TypeScript catches any unsafe mutations on the object.
-
修改架构的所有模型中的特定操作
¥Modify a specific operation in all models of your schema
要扩展架构的所有模型中的查询,请使用 $allModels
而不是特定模型名称。例如:
¥To extend the queries in all the models of your schema, use $allModels
instead of a specific model name. For example:
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)
},
},
},
})
修改特定模型中的所有操作
¥Modify all operations in a specific model
使用 $allOperations
扩展特定模型中的所有操作。
¥Use $allOperations
to extend all operations in a specific model.
例如,以下代码将自定义查询应用于 user
模型上的所有操作:
¥For example, the following code applies a custom query to all operations on the user
model:
const prisma = new PrismaClient().$extends({
query: {
user: {
$allOperations({ model, operation, args, query }) {
/* your custom logic here */
return query(args)
},
},
},
})
修改所有 Prisma Client 操作
¥Modify all Prisma Client operations
使用 $allOperations
方法修改 Prisma 客户端中存在的所有查询方法。$allOperations
可用于模型操作和原始查询。
¥Use the $allOperations
method to modify all query methods present in Prisma Client. The $allOperations
can be used on both model operations and raw queries.
你可以按如下方式修改所有方法:
¥You can modify all methods as follows:
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
。
¥In the event a raw query is invoked, the model
argument passed to the callback will be undefined
.
例如,你可以使用 $allOperations
方法来记录查询,如下所示:
¥For example, you can use the $allOperations
method to log queries as follows:
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
},
},
})
修改架构的所有模型中的所有操作
¥Modify all operations in all models of your schema
使用 $allModels
和 $allOperations
扩展架构的所有模型中的所有操作。
¥Use $allModels
and $allOperations
to extend all operations in all models of your schema.
要将自定义查询应用于架构的所有模型上的所有操作:
¥To apply a custom query to all operations on all models of your 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)
},
},
},
})
修改顶层原始查询操作
¥Modify a top-level raw query operation
要将自定义行为应用于特定的顶层原始查询操作,请使用顶层原始查询函数的名称而不是模型名称:
¥To apply custom behavior to a specific top-level raw query operation, use the name of a top-level raw query function instead of a model name:
- Relational databases
- MongoDB
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)
},
},
})
const prisma = new PrismaClient().$extends({
query: {
$runCommandRaw({ args, query, operation }) {
// handle $runCommandRaw operation
return query(args)
},
},
})
改变查询的结果
¥Mutate the result of a query
你可以使用 await
,然后更改 query
promise 的结果。
¥You can use await
and then mutate the result of the 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
组件类型在查询执行后进行计算。
¥We include the above example to show that this is possible. However, for performance reasons we recommend that you use the result
component type to override existing fields. The result
component type usually gives better performance in this situation because it computes only on access. The query
component type computes after query execution.
将查询封装到批处理事务中
¥Wrap a query into a batch transaction
你可以将扩展查询封装到 批量事务。例如,你可以使用它来制定行级安全性 (RLS)。
¥You can wrap your extended queries into a batch transaction. For example, you can use this to enact row-level security (RLS).
以下示例扩展了 findFirst
,以便它在批处理事务中运行。
¥The following example extends findFirst
so that it runs in a batch transaction.
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)
查询扩展与中间件
¥Query extensions versus middlewares
你可以使用查询扩展或 middlewares 钩子查询生命周期并修改传入查询或其结果。客户端扩展和中间件在以下方面有所不同:
¥You can use query extensions or middlewares to hook into the query life-cycle and modify an incoming query or its result. Client extensions and middlewares differ in the following ways:
-
中间件始终全局地应用于同一个客户端。客户端扩展是独立的,除非你故意将它们组合起来。了解有关客户端扩展的更多信息。
¥Middlewares always apply globally to the same client. Client extensions are isolated, unless you deliberately combine them. Learn more about client extensions.
-
例如,在行级安全性 (RLS) 场景中,你可以将每个用户保留在完全独立的客户端中。使用中间件,所有用户都在同一个客户端中活动。
¥For example, in a row-level security (RLS) scenario, you can keep each user in an entirely separate client. With middlewares, all users are active in the same client.
-
-
在应用执行期间,通过扩展,你可以从一个或多个扩展客户端或标准 Prisma 客户端中进行选择。使用中间件,你无法选择使用哪个客户端,因为只有一个全局客户端。
¥During application execution, with extensions you can choose from one or more extended clients, or the standard Prisma Client. With middlewares, you cannot choose which client to use, because there is only one global client.
-
扩展受益于端到端类型安全和推断,但中间件则不然。
¥Extensions benefit from end-to-end type safety and inference, but middlewares don't.
你可以在所有可以使用中间件的场景中使用 Prisma 客户端扩展。
¥You can use Prisma Client extensions in all scenarios where middlewares can be used.
如果你使用 query
扩展组件和中间件
¥If you use the query
extension component and middlewares
如果你在项目中使用 query
扩展组件和中间件,则适用以下规则和优先级:
¥If you use the query
extension component and middlewares in your project, then the following rules and priorities apply:
-
在你的应用代码中,你必须在主 Prisma 客户端实例上声明所有中间件。你不能在扩展客户端上声明它们。
¥In your application code, you must declare all your middlewares on the main Prisma Client instance. You cannot declare them on an extended client.
-
在执行具有
query
组件的中间件和扩展的情况下,Prisma 客户端会先执行中间件,然后再执行具有query
组件的扩展。Prisma Client 按照你使用$use
或$extends
实例化各个中间件和扩展的顺序执行它们。¥In situations where middlewares and extensions with a
query
component execute, Prisma Client executes the middlewares before it executes the extensions with thequery
component. Prisma Client executes the individual middlewares and extensions in the order in which you instantiated them with$use
or$extends
.