拡張機能
Prisma Client 拡張機能は、バージョン 4.16.0 以降で一般公開されています。これらはバージョン 4.7.0 でプレビューとして導入されました。バージョン 4.16.0 より前のバージョンを使用している場合は、`clientExtensions` プレビュー機能フラグを有効にしてください。
Prisma Client 拡張機能を使用すると、モデル、結果オブジェクト、クエリに機能を追加したり、クライアントレベルのメソッドを追加したりできます。
拡張機能は、以下のコンポーネントタイプのいずれかまたは複数を使用して作成できます。
- `model`: モデルにカスタムメソッドまたはフィールドを追加する
- `client`: Prisma Client にクライアントレベルのメソッドを追加する
- `query`: カスタム Prisma Client クエリを作成する
- `result`: クエリ結果にカスタムフィールドを追加する
例えば、`model` と `client` コンポーネントタイプを使用する拡張機能を作成することができます。
Prisma Client 拡張機能について
Prisma Client 拡張機能を使用すると、拡張クライアントを作成できます。拡張クライアントは、1つ以上の拡張機能によってラップされた標準の Prisma Client の軽量な派生です。標準クライアントは変更されません。プロジェクトには、必要なだけ多くの拡張クライアントを追加できます。拡張クライアントについて詳しく学ぶ。
単一の拡張機能、または複数の拡張機能を拡張クライアントに関連付けることができます。複数の拡張機能について詳しく学ぶ。
他の Prisma ORM ユーザーとPrisma Client 拡張機能を共有したり、他のユーザーが開発した Prisma Client 拡張機能をPrisma ORM プロジェクトにインポートしたりできます。
拡張クライアント
拡張クライアントは、相互に、そして標準クライアントと次のようにやり取りします。
- 各拡張クライアントは、分離されたインスタンスで独立して動作します。
- 拡張クライアントは、相互に、または標準クライアントと競合することはありません。
- すべての拡張クライアントと標準クライアントは、同じPrisma ORM クエリエンジンと通信します。
- すべての拡張クライアントと標準クライアントは、同じ接続プールを共有します。
注: 拡張機能の作成者は、拡張機能の一部として任意のコードを実行できるため、この動作を変更できます。例えば、拡張機能が全く新しい
PrismaClient
インスタンス(独自のクエリエンジンと接続プールを含む)を実際に作成する場合があります。使用する拡張機能のドキュメントを確認し、実装されている可能性のある特定の動作について学んでください。
拡張クライアントの利用例
拡張クライアントは分離されたインスタンスで動作するため、例えば以下のことを行うのに適しています。
- 行レベルセキュリティ (RLS) を実装する。各 HTTP リクエストは独自の RLS 拡張機能を持つクライアントを持ち、セッションデータでカスタマイズされる。これにより、各ユーザーを完全に分離し、それぞれを別々のクライアントに保つことができる。
User
モデルにuser.current()
メソッドを追加して、現在ログインしているユーザーを取得する。- デバッグクッキーが設定されている場合、リクエストのより詳細なロギングを有効にする。
- すべてのログに一意のリクエスト ID を付加して、後で関連付けられるようにする(例:Prisma Client が実行する操作の分析を支援するため)。
- アプリケーションが管理者エンドポイントを呼び出し、ユーザーが必要な権限を持っている場合を除き、モデルから
delete
メソッドを削除する。
Prisma Client に拡張機能を追加する
拡張機能は主に2つの方法で作成できます。
-
クライアントレベルの
$extends
メソッドを使用するconst prisma = new PrismaClient().$extends({
name: 'signUp', // Optional: name appears in error logs
model: { // This is a `model` component
user: { ... } // The extension logic for the `user` model goes inside the curly braces
},
}) -
Prisma.defineExtension
メソッドを使用して拡張機能を定義し、変数に割り当ててから、その拡張機能をクライアントレベルの$extends
メソッドに渡すimport { Prisma } from '@prisma/client'
// Define the extension
const myExtension = Prisma.defineExtension({
name: 'signUp', // Optional: name appears in error logs
model: { // This is a `model` component
user: { ... } // The extension logic for the `user` model goes inside the curly braces
},
})
// Pass the extension to a Prisma Client instance
const prisma = new PrismaClient().$extends(myExtension)ヒントこのパターンは、プロジェクト内で拡張機能を複数のファイルやディレクトリに分離したい場合に便利です。
上記の例では、model
拡張コンポーネントを使用して User
モデルを拡張しています。
$extends
メソッドでは、適切な拡張コンポーネント(model
、client
、result
、またはquery
)を使用してください。
エラーログ用の拡張機能に名前を付ける
エラーログで拡張機能を識別しやすくするために、名前を付けることができます。そのためには、オプションのフィールド name
を使用します。例:
const prisma = new PrismaClient().$extends({
name: `signUp`, // (Optional) Extension name
model: {
user: { ... }
},
})
複数の拡張機能
拡張機能を拡張クライアントに関連付けるには、2つの方法があります。
- 拡張機能単体を拡張クライアントに関連付ける、または
- 拡張機能を他の拡張機能と結合し、これらすべての拡張機能を1つの拡張クライアントに関連付けることができます。これらの結合された拡張機能の機能は、同じ拡張クライアントに適用されます。注: 結合された拡張機能は競合する可能性があります。
上記の2つのアプローチを組み合わせることもできます。例えば、1つの拡張機能をそれ自身の拡張クライアントに関連付け、他の2つの拡張機能を別の拡張クライアントに関連付けることができます。クライアントインスタンスがどのように相互作用するかについて詳しく学ぶ。
拡張クライアントに複数の拡張機能を適用する
以下の例では、2つの拡張機能 extensionA
と extensionB
があると仮定します。これらを組み合わせるには2つの方法があります。
オプション1:新しいクライアントを1行で宣言する
このオプションでは、両方の拡張機能を新しいクライアントに1行のコードで適用します。
// First of all, store your original Prisma Client in a variable as usual
const prisma = new PrismaClient()
// Declare an extended client that has an extensionA and extensionB
const prismaAB = prisma.$extends(extensionA).$extends(extensionB)
その後、コード内で prismaAB
を参照できます。例:prismaAB.myExtensionMethod()
。
オプション2:複数の拡張クライアントを宣言する
このオプションの利点は、拡張クライアントのいずれかを個別に呼び出せることです。
// First of all, store your original Prisma Client in a variable as usual
const prisma = new PrismaClient()
// Declare an extended client that has extensionA applied
const prismaA = prisma.$extends(extensionA)
// Declare an extended client that has extensionB applied
const prismaB = prisma.$extends(extensionB)
// Declare an extended client that is a combination of clientA and clientB
const prismaAB = prismaA.$extends(extensionB)
コード内で、これらのクライアントのいずれかを個別に呼び出すことができます。例:prismaA.myExtensionMethod()
、prismaB.myExtensionMethod()
、または prismaAB.myExtensionMethod()
。
結合された拡張機能における競合
2つ以上の拡張機能を単一の拡張クライアントに結合する場合、競合が発生した際には、最後に宣言した拡張機能が優先されます。上記のオプション1の例で、extensionA
に myExtensionMethod()
というメソッドが定義されており、extensionB
にも myExtensionMethod()
というメソッドがあるとします。prismaAB.myExtensionMethod()
を呼び出すと、Prisma Client は extensionB
で定義された myExtensionMethod()
を使用します。
拡張クライアントの型
拡張された Prisma Client インスタンスの型は、typeof
ユーティリティを使用して次のように推論できます。
const extendedPrismaClient = new PrismaClient().$extends({
/** extension */
})
type ExtendedPrismaClient = typeof extendedPrismaClient
Prisma Client をシングルトンとして使用している場合、拡張された Prisma Client インスタンスの型は、typeof
とReturnType
ユーティリティを使用して次のように取得できます。
function getExtendedClient() {
return new PrismaClient().$extends({
/* extension */
})
}
type ExtendedPrismaClient = ReturnType<typeof getExtendedClient>
Prisma.Result
を使用してモデル型を拡張する
Prisma.Result
型ユーティリティを使用すると、モデル型を拡張して、クライアント拡張機能によって追加されたプロパティを含めることができます。これにより、拡張されたモデルの型(拡張されたプロパティを含む)を推論できます。
例
次の例は、Prisma.Result
を使用して User
モデル型を拡張し、クライアント拡張機能によって追加された __typename
プロパティを含める方法を示しています。
import { PrismaClient, Prisma } from '@prisma/client'
const prisma = new PrismaClient().$extends({
result: {
user: {
__typename: {
needs: {},
compute() {
return 'User'
},
},
},
},
})
type ExtendedUser = Prisma.Result<typeof prisma.user, { select: { id: true } }, 'findFirstOrThrow'>
async function main() {
const user: ExtendedUser = await prisma.user.findFirstOrThrow({
select: {
id: true,
__typename: true,
},
})
console.log(user.__typename) // Output: 'User'
}
main()
Prisma.Result
型ユーティリティは、拡張された User
モデルの型(クライアント拡張機能によって追加された __typename
プロパティを含む)を推論するために使用されます。
制限事項
拡張クライアントでの $on
と $use
の使用法
$on
と $use
は拡張クライアントでは利用できません。これらのクライアントレベルのメソッドを拡張クライアントで引き続き使用したい場合は、クライアントを拡張する前にそれらをフックアップする必要があります。
const prisma = new PrismaClient()
prisma.$use(async (params, next) => {
console.log('This is middleware!')
return next(params)
})
const xPrisma = prisma.$extends({
name: 'myExtension',
model: {
user: {
async signUp(email: string) {
await prisma.user.create({ data: { email } })
},
},
},
})
詳細については、$on
と $use
に関するドキュメントを参照してください。
拡張クライアントでのクライアントレベルメソッドの使用法
クライアントレベルのメソッドは、必ずしも拡張クライアント上に存在するわけではありません。これらのクライアントを使用する前に、まず存在を確認する必要があります。
const xPrisma = new PrismaClient().$extends(...);
if (xPrisma.$connect) {
xPrisma.$connect()
}
ネストされた操作での使用法
query
拡張タイプは、ネストされた読み取りおよび書き込み操作をサポートしていません。