跳到主要內容

`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()findFirstfindManycountcreate。以下範例修改 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 中的任何屬性。例外:您不能變更 includeselect,因為這會更改預期的輸出類型並破壞類型安全。

  • 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)
},
},
},
})

修改頂級原始查詢操作

要將自訂行為應用於特定的頂級原始查詢操作,請使用頂級原始查詢函數的名稱而不是模型名稱

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)
},
},
})

變更查詢結果

您可以使用 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 實例化它們的順序執行個別的中介軟體和擴充功能。