ミドルウェア
非推奨: ミドルウェアはバージョン4.16.0で非推奨になりました。
ミドルウェアの代替として、Prisma Client拡張機能の`query`コンポーネントタイプの使用を推奨します。Prisma Client拡張機能は、バージョン4.7.0でプレビューとして初めて導入され、4.16.0で一般提供されました。
Prisma Client拡張機能を使用すると、独立したPrisma Clientインスタンスを作成し、各クライアントを特定のフィルターまたはユーザーにバインドできます。たとえば、ユーザー分離を提供するために、クライアントを特定のユーザーにバインドできます。Prisma Client拡張機能は、エンドツーエンドの型安全性も提供します。
ミドルウェアは、クエリレベルのライフサイクル・フックとして機能し、クエリが実行される前または後にアクションを実行できます。次のように、`prisma.$use`メソッドを使用してミドルウェアを追加します
const prisma = new PrismaClient()
// Middleware 1
prisma.$use(async (params, next) => {
// Manipulate params here
const result = await next(params)
// See results here
return result
})
// Middleware 2
prisma.$use(async (params, next) => {
// Manipulate params here
const result = await next(params)
// See results here
return result
})
// Queries here
バッチトランザクションを使用する場合、ミドルウェア内で`next`を複数回呼び出さないでください。これにより、トランザクションが中断され、予期しない結果につながります。
`params`はクエリ名など、ミドルウェアで利用可能なパラメーターを表し、`next`はスタック内の次のミドルウェア、*または*元のPrisma Clientクエリを表します。
ミドルウェアの可能なユースケースには以下が含まれます
- フィールド値の設定または上書き - たとえば、ブログ投稿コメントのコンテキスト言語の設定
- 入力データの検証 - たとえば、外部サービスを介してユーザー入力に不適切な言語がないかチェックする
- `delete`クエリをインターセプトし、`update`に変更してソフトデリートを実行する
- クエリの実行にかかった時間をログに記録する
ミドルウェアには他にも多くのユースケースがあります。このリストは、ミドルウェアが対処するように設計された問題の種類のインスピレーションとして役立ちます。
サンプル
以下のサンプルシナリオは、実際にミドルウェアを使用する方法を示しています
ミドルウェアの例:ソフトデリート
以下のサンプルは、ミドルウェアを使用してソフトデリートを実行する方法を示しています。ソフトデリートとは、レコードが実際にデータベースから削除されるのではなく、`deleted`のようなフィールドを`true`に変更することで削除済みとしてマークされることを意味します。ソフトデリートを使用する理由には以下が含まれます
ミドルウェアの例:ロギング
以下の例は、Prisma Clientクエリの実行にかかった時間をログに記録します
ミドルウェアの例:セッションデータ
以下の例は、各Postの言語フィールドをコンテキスト言語(たとえば、セッション状態から取得)に設定します
ミドルウェアを追加する場所
Prisma Clientミドルウェアは**リクエストハンドラのコンテキスト外**に追加してください。そうしないと、各リクエストがミドルウェアの新しい*インスタンス*をスタックに追加してしまいます。以下の例は、ExpressアプリケーションのコンテキストでPrisma Clientミドルウェアを追加する場所を示しています
import express from 'express'
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
prisma.$use(async (params, next) => {
// Manipulate params here
const result = await next(params)
// See results here
return result
})
const app = express()
app.get('/feed', async (req, res) => {
// NO MIDDLEWARE HERE
const posts = await prisma.post.findMany({
where: { published: true },
include: { author: true },
})
res.json(posts)
})
実行順序とミドルウェアスタック
複数のミドルウェアがある場合、**各個別のクエリ**の実行順序は次のとおりです
- 各ミドルウェアにおける`await next(params)`**より前の**すべてのロジック(降順)
- 各ミドルウェアにおける`await next(params)`**より後の**すべてのロジック(昇順)
スタック内の位置に応じて、`await next(params)`は次のいずれかの動作をします
- 次のミドルウェアを実行する(例のミドルウェア #1 および #2 の場合)*または*
- 元のPrisma Clientクエリを実行する(ミドルウェア #3 の場合)
const prisma = new PrismaClient()
// Middleware 1
prisma.$use(async (params, next) => {
console.log(params.args.data.title)
console.log('1')
const result = await next(params)
console.log('6')
return result
})
// Middleware 2
prisma.$use(async (params, next) => {
console.log('2')
const result = await next(params)
console.log('5')
return result
})
// Middleware 3
prisma.$use(async (params, next) => {
console.log('3')
const result = await next(params)
console.log('4')
return result
})
const create = await prisma.post.create({
data: {
title: 'Welcome to Prisma Day 2020',
},
})
const create2 = await prisma.post.create({
data: {
title: 'How to Prisma!',
},
})
出力
Welcome to Prisma Day 2020
1
2
3
4
5
6
How to Prisma!
1
2
3
4
5
6
パフォーマンスと適切なユースケース
ミドルウェアは**すべての**クエリに対して実行されるため、過度な使用はパフォーマンスに悪影響を及ぼす可能性があります。パフォーマンスオーバーヘッドを避けるためには
-
ミドルウェアの早い段階で`params.model`と`params.action`プロパティをチェックし、不要なロジックの実行を避ける
prisma.$use(async (params, next) => {
if (params.model == 'Post' && params.action == 'delete') {
// Logic only runs for delete action and Post model
}
return next(params)
}) -
ミドルウェアがシナリオに適切なソリューションであるかどうかを検討してください。たとえば
- フィールドに値を設定する必要がある場合、`@default`属性を使用できますか?
- `DateTime`フィールドの値を設定する必要がある場合、`now()`関数または`@updatedAt`属性を使用できますか?
- より複雑な検証を実行する必要がある場合、データベース自体で`CHECK`制約を使用できますか?