跳到主要内容

result:为查询结果添加自定义字段和方法

信息

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

您可以使用 result Prisma Client 扩展组件类型,为查询结果添加自定义字段和方法。

使用 $extends 客户端级别方法来创建一个 *扩展客户端*。扩展客户端是标准 Prisma Client 的一个变体,它被一个或多个扩展所包装。

要为查询结果添加自定义字段方法,请使用以下结构。在此示例中,我们向 user 模型查询的结果添加自定义字段 myComputedField

const prisma = new PrismaClient().$extends({
name?: 'name',
result?: {
user: { // in this case, we extend the `user` model
myComputedField: { // the name of the new computed field
needs: { ... },
compute() { ... }
},
},
},
});

参数如下

  • name:(可选)为扩展指定一个名称,该名称将显示在错误日志中。
  • result:定义查询结果的新字段和方法。
  • needs:一个对象,描述结果字段的依赖项。
  • compute:一个方法,定义虚拟字段在访问时如何计算。

为查询结果添加自定义字段

您可以使用 result 扩展组件为查询结果添加字段。这些字段在运行时计算,并且是类型安全的。

在以下示例中,我们为 user 模型添加了一个名为 fullName 的新虚拟字段。

const prisma = new PrismaClient().$extends({
result: {
user: {
fullName: {
// the dependencies
needs: { firstName: true, lastName: true },
compute(user) {
// the computation logic
return `${user.firstName} ${user.lastName}`
},
},
},
},
})

const user = await prisma.user.findFirst()

// return the user's full name, such as "John Doe"
console.log(user.fullName)

在上述示例中,compute 的输入 user 会根据 needs 中定义的对象自动推断类型。firstNamelastName 的类型为 string,因为它们在 needs 中指定。如果它们未在 needs 中指定,则无法访问它们。

在另一个计算字段中重用计算字段

以下示例以类型安全的方式计算用户的称谓和全名。titleFullName 是一个计算字段,它重用了 fullName 计算字段。

const prisma = new PrismaClient()
.$extends({
result: {
user: {
fullName: {
needs: { firstName: true, lastName: true },
compute(user) {
return `${user.firstName} ${user.lastName}`
},
},
},
},
})
.$extends({
result: {
user: {
titleFullName: {
needs: { title: true, fullName: true },
compute(user) {
return `${user.title} (${user.fullName})`
},
},
},
},
})

字段注意事项

  • 出于性能原因,Prisma Client 在访问时计算结果,而不是在检索时计算。

  • 您只能创建基于标量字段的计算字段。

  • 您只能将计算字段与 select 结合使用,并且不能对其进行聚合。例如

    const user = await prisma.user.findFirst({
    select: { email: true },
    })
    console.log(user.fullName) // undefined

为结果对象添加自定义方法

您可以使用 result 组件为查询结果添加方法。以下示例为结果对象添加了一个新方法 save

const prisma = new PrismaClient().$extends({
result: {
user: {
save: {
needs: { id: true },
compute(user) {
return () =>
prisma.user.update({ where: { id: user.id }, data: user })
},
},
},
},
})

const user = await prisma.user.findUniqueOrThrow({ where: { id: someId } })
user.email = 'mynewmail@mailservice.com'
await user.save()

omit 查询选项与 result 扩展组件结合使用

您可以将 omit(预览)选项自定义字段以及自定义字段所需的字段结合使用。

从查询结果中 omit 自定义字段所需的字段

如果您 omit 一个作为自定义字段依赖项的字段,即使它不包含在查询结果中,它仍将从数据库中读取。

以下示例 omitpassword 字段,它是自定义字段 sanitizedPassword 的一个依赖项

const xprisma = prisma.$extends({
result: {
user: {
sanitizedPassword: {
needs: { password: true },
compute(user) {
return sanitize(user.password)
},
},
},
},
})

const user = await xprisma.user.findFirstOrThrow({
omit: {
password: true,
},
})

在这种情况下,尽管 password 从结果中被 omit 掉,但它仍然会从数据库中查询,因为它属于 sanitizedPassword 自定义字段的依赖项。

从查询结果中 omit 自定义字段及其依赖项

为确保省略的字段完全不从数据库中查询,您必须同时 omit 自定义字段及其依赖项。

以下示例同时 omit 了自定义字段 sanitizedPassword 和依赖字段 password

const xprisma = prisma.$extends({
result: {
user: {
sanitizedPassword: {
needs: { password: true },
compute(user) {
return sanitize(user.password)
},
},
},
},
})

const user = await xprisma.user.findFirstOrThrow({
omit: {
sanitizedPassword: true,
password: true,
},
})

在这种情况下,同时 omit passwordsanitizedPassword 将导致两者都从结果中排除,并阻止 password 字段从数据库中读取。

限制

目前,Prisma Client 的结果扩展组件不支持关系字段。这意味着您无法基于关系型关联中的相关模型或字段(例如 user.posts、post.author)创建自定义字段或方法。needs 参数只能引用同一模型中的标量字段。请关注 GitHub 上的 #20091 问题

const prisma = new PrismaClient().$extends({
result: {
user: {
postsCount: {
needs: { posts: true }, // This will not work because posts is a relation field
compute(user) {
return user.posts.length; // Accessing a relation is not allowed
},
},
},
},
})
© . All rights reserved.