Prisma Client拡張機能(プレビュー版)により、多くの新しいユースケースが可能になります。この記事では、拡張機能を使用してPrisma Clientにカスタム機能を追加するさまざまな方法について説明します。
目次
はじめに
Prisma Client拡張機能は、Prismaに型安全な方法で機能を追加するための強力な新しい方法を提供します。これらを使用することで、ORMでネイティブにサポートされていない問題(まだ)に対するシンプルで柔軟なソリューションを作成できます。拡張機能はTypeScriptまたはJavaScriptで定義でき、それらを構成し、異なる拡張機能を持つ複数の軽量なPrisma Clientインスタンスを作成することもできます。
準備ができたら、コードスニペットとして、またはパッケージ化してnpmに公開することで、拡張機能をコミュニティと共有できます。この記事では、拡張機能で何ができるかを示し、あなた自身で作成して共有するきっかけとなることを願っています!
注:Prisma Client拡張機能は、Prismaを使用する際に多くの新しい可能性を開くと信じています。ただし、拡張機能で解決できる問題だからといって、それがファーストクラスの機能として決して対処されないという意味ではありません。私たちの目標の1つは、Prismaにネイティブに統合する前に、コミュニティと共にソリューションを実験し、探索することです。
Prisma Client拡張機能の使用
Prisma Client拡張機能を使用するには、まずPrismaスキーマファイルでclientExtensions
プレビュー機能を有効にする必要があります
次に、Prisma Clientインスタンスで$extends
メソッドを呼び出すことができます。これにより、元のインスタンスを変更することなく、新しい「拡張された」クライアントインスタンスが返されます。$extends
への呼び出しを連鎖させて複数の拡張機能を使用し、異なる拡張機能を持つ個別のインスタンスを作成できます
拡張機能のコンポーネント
拡張機能には、4つの異なるタイプのコンポーネントを含めることができます
- モデルコンポーネントを使用すると、モデルに新しいメソッドを追加できます。これは、
findMany
、create
などのデフォルトメソッドと並行して新しい操作を追加する便利な方法です。これを共通のクエリメソッドのリポジトリとして使用したり、モデルのビジネスロジックをカプセル化したり、クラスの静的メソッドで行う可能性のあるあらゆることを行うことができます。 - クライアントコンポーネントは、Prisma Client自体に新しいトップレベルメソッドを追加するために使用できます。特定のモデルに縛られない機能でクライアントを拡張するためにこれを使用してください。
- クエリコンポーネントを使用すると、クエリのライフサイクルにフックし、サイドエフェクトを実行したり、クエリ引数を変更したり、結果を型安全な方法で変更したりできます。これらは、完全な型安全性を提供し、異なる拡張クライアントインスタンスにアドホックに適用できるミドルウェアの代替です。
- 結果コンポーネントは、クエリ結果オブジェクトにカスタムフィールドとメソッドを追加します。これらを使用すると、仮想/計算フィールドを実装したり、モデルインスタンスのビジネスロジックを一箇所で定義したり、クエリによって返されるデータを変換したりできます。
単一の拡張機能には、1つ以上のコンポーネントと、エラーメッセージに表示するオプションの名前を含めることができます
各タイプの拡張コンポーネントを定義するための完全な構文については、ドキュメントを参照してください。
拡張機能の共有
Prisma.defineExtension
ユーティリティを使用して、他のユーザーと共有できる拡張機能を定義できます
共有拡張機能をnpmに公開する際は、prisma-extension-<package-name>
という命名規則を使用することをお勧めします。これにより、ユーザーは自分のアプリで拡張機能を見つけ、インストールしやすくなります。
たとえば、パッケージ名prisma-extension-find-or-create
で拡張機能を公開すると、ユーザーは次のようにインストールできます
そして、アプリで拡張機能を使用します
詳細については、拡張機能の共有に関するドキュメントをお読みください。
使用例
拡張機能で解決できるユースケースのリストをまとめ、これらの拡張機能がどのように記述できるかの例を作成しました。これらのユースケースとその実装を見てみましょう
注:Prisma Client拡張機能はまだプレビュー版であり、以下の例にはいくつかの制限がある場合があります。既知の注意点については、GitHubの例の
README
ファイルに記載されています。
例:計算フィールド
GitHubで完全な例を見る
この例は、Prismaモデルに仮想/計算フィールドを追加するPrisma Client拡張機能を作成する方法を示しています。これらのフィールドはデータベースには含まれず、実行時に計算されます。
計算フィールドは型安全であり、単純な値から複雑なオブジェクト、さらにはモデルのインスタンスメソッドとして機能する関数まで、あらゆるものを返すことができます。計算フィールドは、依存する他のフィールドを指定する必要があり、他の計算フィールドによって構成/再利用されることがあります。
例:変換されたフィールド
GitHubで完全な例を見る
この例は、Prisma Client拡張機能を使用して、Prismaクエリによって返される結果のフィールドを変換する方法を示しています。この例では、date
フィールドが特定のロケールに合わせて相対的な文字列に変換されます。
これは、アプリケーションのデータアクセス層で国際化(i18n)を実装する方法を示しています。ただし、この手法により、クエリ結果のフィールドに対して、あらゆる種類のカスタム変換やシリアライゼーション/デシリアライゼーションを実装できます。
例:難読化されたフィールド
GitHubで完全な例を見る
この例は、前の変換されたフィールドの例の特殊なケースです。拡張機能を使用して、User
モデル上の機密性の高いpassword
フィールドを非表示にします。password
列は、基になるSQLクエリで選択された列には含まれず、ユーザー結果オブジェクトでアクセスされるとundefined
に解決されます。また、"********"
のような難読化された文字列など、他の値に解決することもできます。
例:インスタンスメソッド
GitHubで完全な例を見る
この例は、Prisma結果オブジェクトにActive Recordのようなインターフェースを追加する方法を示しています。result
拡張機能を使用して、Prisma Clientメソッドによって返されるUser
モデルオブジェクトに直接save
およびdelete
メソッドを追加します。
この手法は、モデルクラスにインスタンスメソッドを追加するのと同様に、Prisma結果オブジェクトを動作でカスタマイズするために使用できます。
例:静的メソッド
GitHubで完全な例を見る
この例は、UserモデルにsignUp()
およびfindManyByDomain()
メソッドを追加するPrisma Client拡張機能を作成する方法を示しています。
この手法は、共通のクエリ/操作のロジックを抽象化したり、リポジトリのようなインターフェースを作成したり、静的クラスメソッドで行う可能性のあるあらゆることを行うために使用できます。
例:モデルフィルタ
GitHubで完全な例を見る
この例は、モデルの再利用可能なフィルタを追加するPrisma Client拡張機能を示しており、これらは構成してクエリのwhere
条件に渡すことができます。複雑で頻繁に使用されるフィルタリング条件は一度記述するだけで、拡張されたPrisma Clientインスタンスを通じて多くのクエリでアクセスできます。
例:読み取り専用クライアント
GitHubで完全な例を見る
この例では、create
やupdate
などの書き込み操作ではなく、findMany
やcount
などの読み取り操作のみを許可するクライアントを作成します。書き込み操作を呼び出すと、実行時およびTypeScriptでのコンパイル時にエラーが発生します。
例:入力変換
GitHubで完全な例を見る
この例では、クエリ引数を変更して、published
された投稿のみを含めるようにする拡張クライアントインスタンスを作成します。
query
拡張機能はクエリ引数の変更を許可するため、このアプローチでさまざまな種類のデフォルトフィルタを適用することが可能です。
例:入力検証
GitHubで完全な例を見る
この例では、Prisma Client拡張機能を使用して、データベースオブジェクトを作成および更新する際にカスタムの実行時検証を実行します。Zodランタイムスキーマを使用して、Prisma書き込みメソッドに渡されたデータが有効であることを確認します。
これは、ユーザー入力をサニタイズしたり、ビジネスロジックルールで定義されたいくつかの基準を満たさないミューテーションを拒否したりするために使用できます。
例:JSONフィールド型
GitHubで完全な例を見る
この次の例は、入力検証と変換されたフィールドの例で示されたアプローチを組み合わせて、Json
フィールドに静的型と実行時型を提供します。Zodを使用してフィールドデータを解析し、静的TypeScript型を推論します。
この例には、JSONプロファイルフィールドを持つUser
モデルが含まれており、その構造はユーザーによって異なる可能性があります。拡張機能は2つの部分から構成されています
- 計算された
profile
フィールドを追加するresult
拡張機能。このフィールドはProfile
Zodスキーマを使用して、基になる型付けされていないprofile
フィールドを解析します。TypeScriptはパーサーから静的データ型を推論するため、クエリ結果には静的および実行時の両方の型安全性があります。 User
モデルのcreate
やupdate
などの書き込みメソッドの入力データに対するprofile
フィールドを解析するquery
拡張機能。
例:クエリロギング
GitHubで完全な例を見る
この例は、Prisma Client拡張機能を使用して、ミドルウェアと同様のタスクを実行する方法を示しています。この例では、query
拡張機能が各クエリの完了にかかる時間を追跡し、その結果とクエリおよび引数自体をログに記録します。
この手法は、一般的なロギング、イベントの発行、使用状況の追跡などに使用できます。
注:パフォーマンスとPrismaがデータベースとどのようにやり取りするかの詳細な洞察を提供するOpenTelemetryトレースとメトリクス機能(両方ともプレビュー版)に興味があるかもしれません。
例:トランザクションのリトライ
GitHubで完全な例を見る
この例は、Prisma Client拡張機能を使用して、書き込み競合/デッドロックタイムアウトが原因で失敗したトランザクションを自動的にリトライする方法を示しています。失敗したトランザクションは、高いトラフィック下での競合を減らすために、指数バックオフとジッターでリトライされます。
例:コールバックフリーのインタラクティブトランザクション
GitHubで完全な例を見る
この例は、コールバックなしでインタラクティブトランザクションを開始するための新しいAPIを追加するPrisma Client拡張機能を示しています。
これにより、インタラクティブトランザクションの全機能(読み取り-変更-書き込みサイクルなど)が、より命令的なAPIで提供されます。これは、一部のシナリオでは、通常のコールバック形式のインタラクティブトランザクションAPIよりも便利かもしれません。
例:監査ログコンテキスト
GitHubで完全な例を見る
この例は、Prisma Client拡張機能を使用して、現在のアプリケーションユーザーのIDをPostgresの監査ログトリガーへのコンテキストとして提供する方法を示しています。ユーザーIDは、テーブル内の行へのすべての変更を追跡する監査証跡に含まれます。
このソリューションの詳細な説明は、GitHubの例のREADME
に記載されています。
例:行レベルセキュリティ
GitHubで完全な例を見る
この例は、Prisma Client拡張機能を使用して、Postgresの行レベルセキュリティ(RLS)を使用してマルチテナントアプリでテナント間のデータを分離する方法を示しています。
このソリューションの詳細な説明は、GitHubの例のREADME
に記載されています。
ご意見をお聞かせください
Prisma Client拡張機能がもたらす可能性に、私たちと同じくらいワクワクしていただけると幸いです!
💡 このGitHub Issueでフィードバックを共有できます。
次の投稿をお見逃しなく!
Prismaニュースレターに登録