`query`:建立自定義 Prisma Client 查詢
Prisma Client 擴充套件在 4.16.0 及更高版本中普遍可用。它們在 4.7.0 版本中以預覽版形式引入。如果您執行的版本早於 4.16.0,請確保啟用 clientExtensions 預覽功能標誌。
您可以使用 query Prisma Client 擴充套件元件型別來接入查詢生命週期,並修改傳入的查詢或其結果。
您可以使用 Prisma Client 擴充套件 query 元件來建立獨立的客戶端。這提供了一種替代中介軟體的方案。您可以將一個客戶端繫結到特定篩選器或使用者,將另一個客戶端繫結到另一個篩選器或使用者。例如,您可能這樣做是為了在行級安全 (RLS) 擴充套件中實現使用者隔離。此外,與中介軟體不同,query 擴充套件元件為您提供端到端的型別安全。瞭解更多關於 query 擴充套件與中介軟體的區別。
擴充套件 Prisma Client 查詢操作
使用 $extends 客戶端級別方法來建立一個擴充套件客戶端。擴充套件客戶端是標準 Prisma Client 的一個變體,它被一個或多個擴充套件包裹。
使用 query 擴充套件元件來修改查詢。您可以按以下方式修改自定義查詢:
要建立自定義查詢,請使用以下結構
const prisma = new PrismaClient().$extends({
name?: 'name',
query?: {
user: { ... } // in this case, we add a query to the `user` model
},
});
屬性如下
name:(可選)指定擴充套件的名稱,該名稱會出現在錯誤日誌中。query:定義一個自定義查詢。
修改特定模型中的特定操作
query 物件可以包含對映到 Prisma Client 操作名稱的函式,例如 findUnique()、findFirst、findMany、count 和 create。以下示例修改 user.findMany,使其使用自定義查詢,只查詢年齡大於 18 歲的使用者。
const prisma = new PrismaClient().$extends({
query: {
user: {
async findMany({ model, operation, args, query }) {
// take incoming `where` and set `age`
args.where = { ...args.where, age: { gt: 18 } }
return query(args)
},
},
},
})
await prisma.user.findMany() // returns users whose age is greater than 18
在上述示例中,呼叫 prisma.user.findMany 會觸發 query.user.findMany。每個回撥函式都會收到一個型別安全的 { model, operation, args, query } 物件,該物件描述了查詢。此物件具有以下屬性:
-
model:我們要擴充套件的查詢的包含模型的名稱。在上述示例中,
model是型別為"User"的字串。 -
operation:正在擴充套件和執行的操作的名稱。在上述示例中,
operation是型別為"findMany"的字串。 -
args:要擴充套件的特定查詢輸入資訊。這是一個型別安全的物件,您可以在查詢發生之前對其進行修改。您可以修改
args中的任何屬性。例外:您不能修改include或select,因為那會改變預期的輸出型別並破壞型別安全。 -
query:查詢結果的 Promise。- 您可以使用
await,然後修改此 Promise 的結果,因為其值是型別安全的。TypeScript 會捕獲物件上的任何不安全修改。
- 您可以使用
修改 Schema 中所有模型中的特定操作
要擴充套件 Schema 中所有模型中的查詢,請使用 $allModels 而不是特定的模型名稱。例如:
const prisma = new PrismaClient().$extends({
query: {
$allModels: {
async findMany({ model, operation, args, query }) {
// set `take` and fill with the rest of `args`
args = { ...args, take: 100 }
return query(args)
},
},
},
})
修改特定模型中的所有操作
使用 $allOperations 來擴充套件特定模型中的所有操作。
例如,以下程式碼將自定義查詢應用於 user 模型上的所有操作:
const prisma = new PrismaClient().$extends({
query: {
user: {
$allOperations({ model, operation, args, query }) {
/* your custom logic here */
return query(args)
},
},
},
})
修改所有 Prisma Client 操作
使用 $allOperations 方法來修改 Prisma Client 中存在的所有查詢方法。$allOperations 可以用於模型操作和原始查詢。
您可以按如下方式修改所有方法:
const prisma = new PrismaClient().$extends({
query: {
$allOperations({ model, operation, args, query }) {
/* your custom logic for modifying all Prisma Client operations here */
return query(args)
},
},
})
如果呼叫了原始查詢,則傳遞給回撥的 model 引數將為 undefined。
例如,您可以使用 $allOperations 方法按如下方式記錄查詢:
const prisma = new PrismaClient().$extends({
query: {
async $allOperations({ operation, model, args, query }) {
const start = performance.now()
const result = await query(args)
const end = performance.now()
const time = end - start
console.log(
util.inspect(
{ model, operation, args, time },
{ showHidden: false, depth: null, colors: true }
)
)
return result
},
},
})
修改 Schema 中所有模型中的所有操作
使用 $allModels 和 $allOperations 來擴充套件 Schema 中所有模型中的所有操作。
將自定義查詢應用於 Schema 中所有模型上的所有操作
const prisma = new PrismaClient().$extends({
query: {
$allModels: {
$allOperations({ model, operation, args, query }) {
/* your custom logic for modifying all operations on all models here */
return query(args)
},
},
},
})
修改頂層原始查詢操作
要將自定義行為應用於特定的頂層原始查詢操作,請使用頂層原始查詢函式的名稱,而不是模型名稱。
- 關係型資料庫
- MongoDB
const prisma = new PrismaClient().$extends({
query: {
$queryRaw({ args, query, operation }) {
// handle $queryRaw operation
return query(args)
},
$executeRaw({ args, query, operation }) {
// handle $executeRaw operation
return query(args)
},
$queryRawUnsafe({ args, query, operation }) {
// handle $queryRawUnsafe operation
return query(args)
},
$executeRawUnsafe({ args, query, operation }) {
// handle $executeRawUnsafe operation
return query(args)
},
},
})
const prisma = new PrismaClient().$extends({
query: {
$runCommandRaw({ args, query, operation }) {
// handle $runCommandRaw operation
return query(args)
},
},
})
修改查詢結果
您可以使用 await,然後修改 query Promise 的結果。
const prisma = new PrismaClient().$extends({
query: {
user: {
async findFirst({ model, operation, args, query }) {
const user = await query(args)
if (user.password !== undefined) {
user.password = '******'
}
return user
},
},
},
})
我們包含上述示例是為了表明這是可能的。然而,出於效能原因,我們建議您使用result 元件型別來覆蓋現有欄位。在這種情況下,result 元件型別通常能提供更好的效能,因為它只在訪問時進行計算。query 元件型別在查詢執行後進行計算。
將查詢包裝成批處理事務
您可以將擴充套件查詢包裝到批處理事務中。例如,您可以使用此功能來實現行級安全 (RLS)。
以下示例擴充套件了 findFirst,使其在批處理事務中執行。
const transactionExtension = Prisma.defineExtension((prisma) =>
prisma.$extends({
query: {
user: {
// Get the input `args` and a callback to `query`
async findFirst({ args, query, operation }) {
const [result] = await prisma.$transaction([query(args)]) // wrap the query in a batch transaction, and destructure the result to return an array
return result // return the first result found in the array
},
},
},
})
)
const prisma = new PrismaClient().$extends(transactionExtension)
查詢擴充套件與中介軟體對比
您可以使用查詢擴充套件或中介軟體來接入查詢生命週期,並修改傳入的查詢或其結果。客戶端擴充套件和中介軟體在以下方面有所不同:
- 中介軟體總是全域性應用於同一個客戶端。客戶端擴充套件是隔離的,除非您有意地組合它們。瞭解更多關於客戶端擴充套件的資訊。
- 例如,在行級安全 (RLS) 場景中,您可以將每個使用者保留在完全獨立的客戶端中。而使用中介軟體時,所有使用者都在同一個客戶端中活動。
- 在應用程式執行期間,使用擴充套件時,您可以選擇一個或多個擴充套件客戶端,或標準 Prisma Client。使用中介軟體時,您無法選擇要使用的客戶端,因為只有一個全域性客戶端。
- 擴充套件受益於端到端的型別安全和型別推斷,而中介軟體則不然。
您可以在所有可以使用中介軟體的場景中使用 Prisma Client 擴充套件。
如果您同時使用 query 擴充套件元件和中介軟體
如果您在專案中同時使用 query 擴充套件元件和中介軟體,則適用以下規則和優先順序:
- 在您的應用程式程式碼中,您必須在主 Prisma Client 例項上宣告所有中介軟體。您不能在擴充套件客戶端上宣告它們。
- 在中介軟體和帶有
query元件的擴充套件執行的情況下,Prisma Client 會在執行帶有query元件的擴充套件之前執行中介軟體。Prisma Client 會按照您使用$use或$extends例項化它們的順序執行各個中介軟體和擴充套件。