跳到主要内容

扩展

信息

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 执行的操作。
  • 除非应用程序调用管理员端点并且用户具有必要的权限,否则从模型中删除 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` 方法中,使用适当的扩展组件(`model`、clientresultquery)。

为错误日志命名扩展

您可以命名您的扩展,以帮助在错误日志中识别它们。为此,请使用可选字段 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 扩展类型不支持嵌套的读取和写入操作。