跳到主要内容

扩展

信息

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 方法中,使用适当的扩展组件(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 扩展类型不支持嵌套读写操作。

© . All rights reserved.