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