2022年12月19日

Prisma Clientの柔軟性が大幅に向上:Prisma Client拡張機能(プレビュー)

Prisma Client拡張機能(プレビュー版)により、多くの新しいユースケースが可能になります。この記事では、拡張機能を使用してPrisma Clientにカスタム機能を追加するさまざまな方法について説明します。

Prisma Client Just Became a Lot More Flexible: Prisma Client Extensions (Preview)

目次

はじめに

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つの異なるタイプのコンポーネントを含めることができます

  • モデルコンポーネントを使用すると、モデルに新しいメソッドを追加できます。これは、findManycreateなどのデフォルトメソッドと並行して新しい操作を追加する便利な方法です。これを共通のクエリメソッドのリポジトリとして使用したり、モデルのビジネスロジックをカプセル化したり、クラスの静的メソッドで行う可能性のあるあらゆることを行うことができます。
  • クライアントコンポーネントは、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で完全な例を見る

この例では、createupdateなどの書き込み操作ではなく、findManycountなどの読み取り操作のみを許可するクライアントを作成します。書き込み操作を呼び出すと、実行時およびTypeScriptでのコンパイル時にエラーが発生します。

例:入力変換

GitHubで完全な例を見る

この例では、クエリ引数を変更して、publishedされた投稿のみを含めるようにする拡張クライアントインスタンスを作成します。

query拡張機能はクエリ引数の変更を許可するため、このアプローチでさまざまな種類のデフォルトフィルタを適用することが可能です。

例:入力検証

GitHubで完全な例を見る

この例では、Prisma Client拡張機能を使用して、データベースオブジェクトを作成および更新する際にカスタムの実行時検証を実行します。Zodランタイムスキーマを使用して、Prisma書き込みメソッドに渡されたデータが有効であることを確認します。

これは、ユーザー入力をサニタイズしたり、ビジネスロジックルールで定義されたいくつかの基準を満たさないミューテーションを拒否したりするために使用できます。

例:JSONフィールド型

GitHubで完全な例を見る

この次の例は、入力検証変換されたフィールドの例で示されたアプローチを組み合わせて、Jsonフィールドに静的型と実行時型を提供します。Zodを使用してフィールドデータを解析し、静的TypeScript型を推論します。

この例には、JSONプロファイルフィールドを持つUserモデルが含まれており、その構造はユーザーによって異なる可能性があります。拡張機能は2つの部分から構成されています

  • 計算されたprofileフィールドを追加するresult拡張機能。このフィールドはProfile Zodスキーマを使用して、基になる型付けされていないprofileフィールドを解析します。TypeScriptはパーサーから静的データ型を推論するため、クエリ結果には静的および実行時の両方の型安全性があります。
  • Userモデルのcreateupdateなどの書き込みメソッドの入力データに対する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ニュースレターに登録

© . All rights reserved.