`result`: 为查询结果添加自定义字段和方法
Prisma Client 扩展自 4.16.0 及更高版本起正式发布。它们在 4.7.0 版本中以预览版形式引入。如果您运行的版本早于 4.16.0,请确保启用 clientExtensions
预览功能标志。
您可以使用 result
Prisma Client 扩展组件类型,为查询结果添加自定义字段和方法。
使用 $extends
客户端级别方法创建扩展客户端。扩展客户端是标准 Prisma Client 的变体,它被一个或多个扩展包装。
要为查询结果添加自定义字段或方法,请使用以下结构。在本示例中,我们将自定义字段 myComputedField
添加到 user
模型查询的结果中。
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
中定义的对象自动进行类型化。firstName
和 lastName
的类型为 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 = '[email protected]'
await user.save()
将 omit
查询选项与 result
扩展组件一起使用
您可以将 omit
(预览版) 选项与自定义字段和自定义字段所需的字段一起使用。
从查询结果中 omit
自定义字段所需的字段
如果您 omit
了作为自定义字段依赖项的字段,即使它不会包含在查询结果中,它仍将从数据库中读取。
以下示例省略了 password
字段,该字段是自定义字段 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
,但由于它是自定义字段 sanitizedPassword
的依赖项,因此仍将从数据库中查询它。
从查询结果中 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,
},
})
在这种情况下,同时省略 password
和 sanitizedPassword
将从结果中排除两者,并防止从数据库中读取 password
字段。
局限性
目前,Prisma Client 的 result 扩展组件不支持关系字段。这意味着您无法基于相关模型或关系关系中的字段(例如,user.posts,post.author)创建自定义字段或方法。 needs 参数只能引用同一模型中的标量字段。请关注 GitHub 上的 issue #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
},
},
},
},
})