`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 會捕獲物件上的任何不安全變更。
- 您可以使用
修改架構中所有模型中的特定操作
要擴充架構中所有模型中的查詢,請使用 $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
},
},
})
修改架構中所有模型中的所有操作
使用 $allModels
和 $allOperations
來擴充架構中所有模型中的所有操作。
要將自訂查詢應用於架構中所有模型上的所有操作
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
實例化它們的順序執行個別的中介軟體和擴充功能。