跳到主要内容

扩展

信息

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

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

您可以使用以下一个或多个组件类型创建扩展

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

关于 Prisma Client 扩展

当您使用 Prisma Client 扩展时,您会创建一个扩展客户端。扩展客户端是标准 Prisma Client 的轻量级变体,它由一个或多个扩展包装。标准客户端不会被修改。您可以根据需要在您的项目中添加任意数量的扩展客户端。了解更多关于扩展客户端的信息

您可以将单个扩展或多个扩展与扩展客户端关联。了解更多关于多个扩展的信息

您可以与其他 Prisma ORM 用户共享您的 Prisma Client 扩展,并将其他用户开发的 Prisma Client 扩展导入到您的 Prisma ORM 项目中

扩展客户端

扩展客户端彼此之间以及与标准客户端的交互方式如下

  • 每个扩展客户端在隔离的实例中独立运行。
  • 扩展客户端之间或与标准客户端之间不会发生冲突。
  • 所有扩展客户端和标准客户端都与同一个Prisma ORM 查询引擎通信。
  • 所有扩展客户端和标准客户端共享同一个连接池。

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

扩展客户端的用例示例

因为扩展客户端在隔离的实例中运行,所以它们可以成为执行以下操作的好方法,例如

  • 实现行级安全性(RLS),其中每个 HTTP 请求都有其自己的客户端,该客户端具有自己的 RLS 扩展,并使用会话数据进行自定义。这可以将每个用户完全隔离,每个用户都在单独的客户端中。
  • User 模型添加 user.current() 方法以获取当前登录的用户。
  • 如果设置了调试 cookie,则为请求启用更详细的日志记录。
  • 将唯一的请求 ID 附加到所有日志,以便您稍后将其关联,例如,帮助您分析 Prisma Client 执行的操作。
  • 除非应用程序调用 admin 端点且用户具有必要的权限,否则从模型中删除 delete 方法。

向 Prisma Client 添加扩展

您可以使用两种主要方法创建扩展

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

    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 方法

    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)
    提示

    当您想要将扩展分隔到项目中的多个文件或目录中时,此模式很有用。

以上示例使用 model 扩展组件来扩展 User 模型。

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

命名扩展以用于错误日志

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

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

多个扩展

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

  • 您可以单独将其与扩展客户端关联,或者
  • 您可以将扩展与其他扩展组合,并将所有这些扩展与扩展客户端关联。这些组合扩展的功能适用于同一扩展客户端。注意:组合扩展可能会发生冲突

您可以结合以上两种方法。例如,您可以将一个扩展与自己的扩展客户端关联,并将另外两个扩展与另一个扩展客户端关联。了解更多关于客户端实例如何交互的信息

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

在以下示例中,假设您有两个扩展,extensionAextensionB。有两种方法可以将它们组合起来。

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

使用此选项,您可以在一行代码中将两个扩展应用于新客户端。

// 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()

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

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

// 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()

组合扩展中的冲突

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

扩展客户端的类型

您可以使用 typeof 工具来推断扩展 Prisma Client 实例的类型,如下所示

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

type ExtendedPrismaClient = typeof extendedPrismaClient

如果您将 Prisma Client 用作单例,则可以使用 typeofReturnType 工具来获取扩展 Prisma Client 实例的类型,如下所示

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

type ExtendedPrismaClient = ReturnType<typeof getExtendedClient>

使用 Prisma.Result 扩展模型类型

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

示例

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

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 属性。

限制

在扩展客户端中使用 $on$use

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

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 的文档

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

客户端级别方法 不一定存在于扩展客户端上。对于这些客户端,您需要先检查是否存在,然后再使用。

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

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

与嵌套操作一起使用

query 扩展类型不支持嵌套的读取和写入操作。