`result`: クエリ結果にカスタムフィールドとメソッドを追加する
Prisma Client拡張は、バージョン4.16.0以降で一般提供されています。これらはバージョン4.7.0でプレビューとして導入されました。バージョン4.16.0より前のバージョンを使用している場合は、`clientExtensions`プレビュー機能フラグを有効にしてください。
`result` Prisma Client拡張コンポーネントタイプを使用して、クエリ結果にカスタムフィールドとメソッドを追加できます。
`$extends` クライアントレベルメソッドを使用して、_拡張クライアント_を作成します。拡張クライアントは、1つ以上の拡張によってラップされた標準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`拡張コンポーネントを使用して、クエリ結果にフィールドを追加できます。これらのフィールドは実行時に計算され、型安全です。
以下の例では、`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`で定義されたオブジェクトに従って自動的に型付けされます。`firstName`と`lastName`は、`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`フィールドを`omit`します。
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`する
`omit`されたフィールドがデータベースからまったくクエリされないようにするには、カスタムフィールドとその依存関係の両方を`omit`する必要があります。
以下の例では、カスタムフィールド`sanitizedPassword`と依存する`password`フィールドの両方を`omit`します。
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`の両方を`omit`すると、両方が結果から除外され、`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
},
},
},
},
})