メインコンテンツにスキップ

`result`: クエリ結果にカスタムフィールドとメソッドを追加する

情報

Prisma Client拡張機能はバージョン4.16.0以降で一般提供されています。これらはバージョン4.7.0でプレビュー導入されました。4.16.0より前のバージョンを実行している場合は、必ずclientExtensionsプレビュー機能フラグを有効にしてください。

result Prisma Client拡張機能コンポーネントタイプを使用して、クエリ結果にカスタムフィールドとメソッドを追加できます。

$extends クライアントレベルメソッドを使用して、拡張クライアントを作成します。拡張クライアントは、1つ以上の拡張機能でラップされた標準の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拡張機能コンポーネントを使用して、クエリ結果にフィールドを追加できます。これらのフィールドは実行時に計算され、タイプセーフです。

次の例では、fullNameという名前の新しい仮想フィールドをuserモデルに追加します。

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は、needsで指定されているため、string型です。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()

result拡張機能コンポーネントでomitクエリオプションを使用する

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: {
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,
},
})

この場合、passwordsanitizedPasswordの両方を省略すると、両方が結果から除外されるだけでなく、passwordフィールドがデータベースから読み取られるのを防ぎます。

制限事項

現在のところ、Prisma Clientの結果拡張機能コンポーネントはリレーションフィールドをサポートしていません。これは、リレーショナルリレーションシップ(例: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
},
},
},
},
})