Skip to main content

扩展

info

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.

你可以使用 Prisma 客户端扩展向模型、结果对象和查询添加功能,或添加客户端级方法。

¥You can use Prisma Client extensions to add functionality to your models, result objects, and queries, or to add client-level methods.

你可以使用以下一种或多种组件类型创建扩展:

¥You can create an extension with one or more of the following component types:

例如,你可以创建使用 modelclient 组件类型的扩展。

¥For example, you might create an extension that uses the model and client component types.

关于 Prisma 客户端扩展

¥About Prisma Client extensions

当你使用 Prisma 客户端扩展时,你将创建一个扩展客户端。扩展客户端是标准 Prisma 客户端的轻量级变体,由一个或多个扩展封装。标准客户端没有发生变化。你可以根据需要向项目中添加任意数量的扩展客户端。了解有关扩展客户的更多信息

¥When you use a Prisma Client extension, you create an extended client. An extended client is a lightweight variant of the standard Prisma Client that is wrapped by one or more extensions. The standard client is not mutated. You can add as many extended clients as you want to your project. Learn more about extended clients.

你可以将单个分机或多个分机与扩展客户端关联。了解有关多个扩展的更多信息

¥You can associate a single extension, or multiple extensions, with an extended client. Learn more about multiple extensions.

你可以与其他 Prisma ORM 用户 分享你的 Prisma 客户端扩展,并 import Prisma Client extensions developed by other users 进入你的 Prisma ORM 项目。

¥You can share your Prisma Client extensions with other Prisma ORM users, and import Prisma Client extensions developed by other users into your Prisma ORM project.

扩展客户

¥Extended clients

扩展客户端相互交互以及与标准客户端交互,如下所示:

¥Extended clients interact with each other, and with the standard client, as follows:

  • 每个扩展客户端在隔离实例中独立运行。

    ¥Each extended client operates independently in an isolated instance.

  • 扩展客户端不能相互冲突,也不能与标准客户端冲突。

    ¥Extended clients cannot conflict with each other, or with the standard client.

  • 所有扩展客户端和标准客户端都与同一个 Prisma ORM 查询引擎 进行通信。

    ¥All extended clients and the standard client communicate with the same Prisma ORM query engine.

  • 所有扩展客户端和标准客户端共享相同的连接池。

    ¥All extended clients and the standard client share the same connection pool.

注意:扩展的作者可以修改此行为,因为他们能够将任意代码作为扩展的一部分运行。例如,扩展实际上可能会创建一个全新的 PrismaClient 实例(包括其自己的查询引擎和连接池)。请务必检查你正在使用的扩展的文档,以了解它可能实现的任何特定行为。

¥Note: The author of an extension can modify this behavior since they're able to run arbitrary code as part of an extension. For example, an extension might actually create an entirely new PrismaClient instance (including its own query engine and connection pool). Be sure to check the documentation of the extension you're using to learn about any specific behavior it might implement.

扩展客户端的示例用例

¥Example use cases for extended clients

由于扩展客户端在隔离实例中运行,因此它们是执行以下操作的好方法,例如:

¥Because extended clients operate in isolated instances, they can be a good way to do the following, for example:

  • 实现行级安全性 (RLS),其中每个 HTTP 请求都有自己的客户端,并具有自己的 RLS 扩展,并使用会话数据进行自定义。这可以使每个用户完全独立,每个用户都在单独的客户端中。

    ¥Implement row-level security (RLS), where each HTTP request has its own client with its own RLS extension, customized with session data. This can keep each user entirely separate, each in a separate client.

  • User 模型添加 user.current() 方法来获取当前登录的用户。

    ¥Add a user.current() method for the User model to get the currently logged-in user.

  • 如果设置了调试 cookie,则为请求启用更详细的日志记录。

    ¥Enable more verbose logging for requests if a debug cookie is set.

  • 将唯一的请求 ID 附加到所有日志,以便你稍后可以将它们关联起来,例如帮助你分析 Prisma 客户端执行的操作。

    ¥Attach a unique request id to all logs so that you can correlate them later, for example to help you analyze the operations that Prisma Client carries out.

  • 从模型中删除 delete 方法,除非应用调用管理端点并且用户具有必要的权限。

    ¥Remove a delete method from models unless the application calls the admin endpoint and the user has the necessary privileges.

添加 Prisma 客户端扩展

¥Add an extension to Prisma Client

你可以使用两种主要方式创建扩展:

¥You can create an extension using two primary ways:

  • 使用客户端级别的 $extends 方法

    ¥Use the client-level $extends method

    const prisma = new PrismaClient().$extends({
    name: 'signUp', // Optional: name appears in error logs
    model: { // This is a `model` component
    user: { ... } // The extension logic for the `user` model goes inside the curly braces
    },
    })
  • 使用 Prisma.defineExtension 方法定义扩展并将其分配给变量,然后将扩展传递给客户端级 $extends 方法

    ¥Use the Prisma.defineExtension method to define an extension and assign it to a variable, and then pass the extension to the client-level $extends method

    import { Prisma } from '@prisma/client'

    // Define the extension
    const myExtension = Prisma.defineExtension({
    name: 'signUp', // Optional: name appears in error logs
    model: { // This is a `model` component
    user: { ... } // The extension logic for the `user` model goes inside the curly braces
    },
    })

    // Pass the extension to a Prisma Client instance
    const prisma = new PrismaClient().$extends(myExtension)
    提示

    当你想要将扩展分成项目内的多个文件或目录时,此模式非常有用。

    ¥This pattern is useful for when you would like to separate extensions into multiple files or directories within a project.

上面的例子使用 model 扩展组件 来扩展 User 模型。

¥The above examples use the model extension component to extend the User model.

$extends 方法中,使用适当的扩展组件(modelclientresultquery)。

¥In your $extends method, use the appropriate extension component or components (model, client, result or query).

为错误日志命名扩展名

¥Name an extension for error logs

你可以命名你的扩展以帮助在错误日志中识别它们。为此,请使用可选字段 name。例如:

¥You can name your extensions to help identify them in error logs. To do so, use the optional field name. For example:

const prisma = new PrismaClient().$extends({
name: `signUp`, // (Optional) Extension name
model: {
user: { ... }
},
})

多种扩展

¥Multiple extensions

你可以通过以下两种方式之一将分机与 扩展客户端 关联:

¥You can associate an extension with an extended client in one of two ways:

  • 你可以将其与扩展客户端单独关联,或者

    ¥You can associate it with an extended client on its own, or

  • 你可以将扩展与其他扩展结合起来,并将所有这些扩展与扩展客户端相关联。这些组合扩展的功能适用于同一扩展客户端。注意:组合扩展可能会发生冲突

    ¥You can combine the extension with other extensions and associate all of these extensions with an extended client. The functionality from these combined extensions applies to the same extended client. Note: Combined extensions can conflict.

你可以结合上面的两种方法。例如,你可以将一个扩展与其自己的扩展客户端关联,并将另外两个扩展与另一个扩展客户端关联。详细了解客户端实例如何交互

¥You can combine the two approaches above. For example, you might associate one extension with its own extended client and associate two other extensions with another extended client. Learn more about how client instances interact.

将多个扩展应用到扩展客户端

¥Apply multiple extensions to an extended client

在以下示例中,假设你有两个分机:extensionAextensionB。有两种方法可以将它们结合起来。

¥In the following example, suppose that you have two extensions, extensionA and extensionB. There are two ways to combine these.

选项 1:在一行中声明新客户端

¥Option 1: Declare the new client in one line

使用此选项,你可以通过一行代码将这两个扩展应用到新客户端。

¥With this option, you apply both extensions to a new client in one line of code.

// First of all, store your original Prisma Client in a variable as usual
const prisma = new PrismaClient()

// Declare an extended client that has an extensionA and extensionB
const prismaAB = prisma.$extends(extensionA).$extends(extensionB)

然后你可以在代码中引用 prismaAB,例如 prismaAB.myExtensionMethod()

¥You can then refer to prismaAB in your code, for example prismaAB.myExtensionMethod().

选项 2:声明多个扩展客户端

¥Option 2: Declare multiple extended clients

此选项的优点是你可以单独调用任何扩展客户端。

¥The advantage of this option is that you can call any of the extended clients separately.

// First of all, store your original Prisma Client in a variable as usual
const prisma = new PrismaClient()

// Declare an extended client that has extensionA applied
const prismaA = prisma.$extends(extensionA)

// Declare an extended client that has extensionB applied
const prismaB = prisma.$extends(extensionB)

// Declare an extended client that is a combination of clientA and clientB
const prismaAB = prismaA.$extends(extensionB)

在你的代码中,你可以单独调用这些客户端中的任何一个,例如 prismaA.myExtensionMethod()prismaB.myExtensionMethod()prismaAB.myExtensionMethod()

¥In your code, you can call any of these clients separately, for example prismaA.myExtensionMethod(), prismaB.myExtensionMethod(), or prismaAB.myExtensionMethod().

组合扩展中的冲突

¥Conflicts in combined extensions

当你将两个或多个扩展组合成一个扩展客户端时,你声明的最后一个扩展在任何冲突中优先。在上面选项 1 的示例中,假设在 extensionA 中定义了一个名为 myExtensionMethod() 的方法,在 extensionB 中定义了一个名为 myExtensionMethod() 的方法。当你调用 prismaAB.myExtensionMethod() 时,Prisma 客户端将使用 extensionB 中定义的 myExtensionMethod()

¥When you combine two or more extensions into a single extended client, then the last extension that you declare takes precedence in any conflict. In the example in option 1 above, suppose there is a method called myExtensionMethod() defined in extensionA and a method called myExtensionMethod() in extensionB. When you call prismaAB.myExtensionMethod(), then Prisma Client uses myExtensionMethod() as defined in extensionB.

扩展客户端类型

¥Type of an extended client

你可以使用 typeof 实用程序推断扩展 Prisma 客户端实例的类型,如下所示:

¥You can infer the type of an extended Prisma Client instance using the typeof utility as follows:

const extendedPrismaClient = new PrismaClient().$extends({
/** extension */
})

type ExtendedPrismaClient = typeof extendedPrismaClient

如果你将 Prisma Client 作为单例使用,则可以使用 typeofReturnType 实用程序获取扩展 Prisma Client 实例的类型,如下所示:

¥If you're using Prisma Client as a singleton, you can get the type of the extended Prisma Client instance using the typeof and ReturnType utilities as follows:

function getExtendedClient() {
return new PrismaClient().$extends({
/* extension */
})
}

type ExtendedPrismaClient = ReturnType<typeof getExtendedClient>

使用 Prisma.Result 扩展模型类型

¥Extending model types with Prisma.Result

你可以使用 Prisma.Result 类型实用程序扩展模型类型以包含通过客户端扩展添加的属性。这允许你推断扩展模型的类型,包括扩展属性。

¥You can use the Prisma.Result type utility to extend model types to include properties added via client extensions. This allows you to infer the type of the extended model, including the extended properties.

示例

¥Example

以下示例演示如何使用 Prisma.Result 扩展 User 模型类型以包含通过客户端扩展添加的 __typename 属性。

¥The following example demonstrates how to use Prisma.Result to extend the User model type to include a __typename property added via a client extension.

import { PrismaClient, Prisma } from '@prisma/client'

const prisma = new PrismaClient().$extends({
result: {
user: {
__typename: {
needs: {},
compute() {
return 'User'
},
},
},
},
})

type ExtendedUser = Prisma.Result<typeof prisma.user, { select: { id: true } }, 'findFirstOrThrow'>

async function main() {
const user: ExtendedUser = await prisma.user.findFirstOrThrow({
select: {
id: true,
__typename: true,
},
})

console.log(user.__typename) // Output: 'User'
}

main()

Prisma.Result 类型实用程序用于推断扩展的 User 模型的类型,包括通过客户端扩展添加的 __typename 属性。

¥The Prisma.Result type utility is used to infer the type of the extended User model, including the __typename property added via the client extension.

局限性

¥Limitations

$on$use 与扩展客户端的使用

¥Usage of $on and $use with extended clients

$on$use 在扩展客户端中不可用。如果你想继续将这些 客户端级别的方法 与扩展客户端一起使用,则需要在扩展客户端之前将它们连接起来。

¥$on and $use are not available in extended clients. If you would like to continue using these client-level methods with an extended client, you will need to hook them up before extending the client.

const prisma = new PrismaClient()

prisma.$use(async (params, next) => {
console.log('This is middleware!')
return next(params)
})

const xPrisma = prisma.$extends({
name: 'myExtension',
model: {
user: {
async signUp(email: string) {
await prisma.user.create({ data: { email } })
},
},
},
})

要了解更多信息,请参阅我们有关 $on$use 的文档

¥To learn more, see our documentation on $on and $use

在扩展客户端中使用客户端级方法

¥Usage of client-level methods in extended clients

客户端级方法 不一定存在于扩展客户端上。对于这些客户端,你需要在使用之前首先检查是否存在。

¥Client-level methods do not necessarily exist on extended clients. For these clients you will need to first check for existence before using.

const xPrisma = new PrismaClient().$extends(...);

if (xPrisma.$connect) {
xPrisma.$connect()
}

与嵌套操作一起使用

¥Usage with nested operations

query 扩展类型不支持嵌套读写操作。

¥The query extension type does not support nested read and write operations.