擴充功能
Prisma Client 擴充功能從 4.16.0 版本開始正式發布(Generally Available)。它們是在 4.7.0 版本中以預覽版(Preview)形式引入的。如果您使用的版本低於 4.16.0,請確保啟用 clientExtensions 預覽功能標記(Preview feature flag)。
您可以使用 Prisma Client 擴充功能為模型、結果物件和查詢新增功能,或新增客戶端層級的方法。
您可以透過以下一種或多種元件類型來建立擴充功能:
model:為您的模型新增自訂方法或欄位client:為 Prisma Client 新增客戶端層級的方法query:建立自訂的 Prisma Client 查詢result:為您的查詢結果新增自訂欄位
例如,您可以建立一個同時使用 model 和 client 元件類型的擴充功能。
關於 Prisma Client 擴充功能
當您使用 Prisma Client 擴充功能時,您會建立一個擴充後的客戶端(extended client)。擴充後的客戶端是標準 Prisma Client 的輕量級變體,由一個或多個擴充功能封裝而成。標準客戶端本身不會被變更。您可以在專案中新增任意數量的擴充後客戶端。深入了解擴充後的客戶端。
您可以將單個或多個擴充功能與一個擴充後的客戶端關聯起來。深入了解多重擴充功能。
您可以與其他 Prisma ORM 使用者分享您的 Prisma Client 擴充功能,並將其他使用者開發的 Prisma Client 擴充功能導入到您的 Prisma ORM 專案中。
擴充後的客戶端
擴充後的客戶端與彼此以及標準客戶端之間的互動方式如下:
- 每個擴充後的客戶端都在獨立的實例中運作。
- 擴充後的客戶端彼此之間,或與標準客戶端之間不會產生衝突。
- 所有擴充後的客戶端和標準客戶端都與同一個 Prisma ORM 查詢引擎進行通訊。
- 所有擴充後的客戶端和標準客戶端共享同一個連線池(connection pool)。
注意:擴充功能的作者可以修改此行為,因為他們可以在擴充功能中執行任意程式碼。例如,一個擴充功能實際上可能會建立一個全新的
PrismaClient實例(包含其自己的查詢引擎和連線池)。請務必查看您所使用擴充功能的文件,以了解其可能實作的任何特定行為。
擴充後客戶端的範例使用場景
由於擴充後的客戶端在獨立的實例中運作,它們在以下場景中非常有用:
- 實作列級安全性 (RLS),每個 HTTP 請求都有其自己的客戶端,並帶有自訂的 RLS 擴充功能以及工作階段資料。這可以將每個使用者完全隔離,每個使用者都在單獨的客戶端中。
- 為
User模型新增一個user.current()方法,以取得目前登入的使用者。 - 如果設定了偵錯 cookie,則為請求啟用更詳細的日誌記錄。
- 將唯一的請求 ID 附加到所有日誌中,以便稍後進行關聯,例如協助您分析 Prisma Client 執行的操作。
- 除非應用程式呼叫管理端點且使用者擁有必要的權限,否則從模型中移除
delete方法。
為 Prisma Client 新增擴充功能
您主要可以透過兩種方式建立擴充功能:
-
使用客戶端層級的
$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: { ... }
},
})
多重擴充功能
您可以透過以下兩種方式之一將擴充功能與擴充後的客戶端關聯起來:
- 您可以將其單獨與一個擴充後的客戶端關聯,或者
- 您可以將該擴充功能與其他擴充功能組合,並將所有這些擴充功能與一個擴充後的客戶端關聯。這些組合後的擴充功能功能將應用於同一個擴充後的客戶端。注意:組合後的擴充功能可能會產生衝突。
您可以組合使用上述兩種方法。例如,您可以將一個擴充功能與其自己的擴充後客戶端關聯,並將另外兩個擴充功能與另一個擴充後客戶端關聯。深入了解客戶端實例如何互動。
將多重擴充功能應用於擴充後的客戶端
在下列範例中,假設您有兩個擴充功能:extensionA 和 extensionB。有兩種方法可以組合它們:
選項 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()。
組合擴充功能中的衝突
當您將兩個或多個擴充功能組合到單一擴充後的客戶端時,最後宣告的擴充功能在發生衝突時具有優先權。在上述選項 1 的範例中,假設 extensionA 定義了一個名為 myExtensionMethod() 的方法,而 extensionB 中也定義了一個名為 myExtensionMethod() 的方法。當您呼叫 prismaAB.myExtensionMethod() 時,Prisma Client 會使用 extensionB 中定義的 myExtensionMethod()。
擴充後客戶端的型別
您可以使用 typeof 工具來推斷擴充後的 Prisma Client 實例的型別,如下所示:
const extendedPrismaClient = new PrismaClient().$extends({
/** extension */
})
type ExtendedPrismaClient = typeof extendedPrismaClient
如果您將 Prisma Client 用作單例(singleton),您可以使用 typeof 和 ReturnType 工具來取得擴充後的 Prisma Client 實例的型別,如下所示:
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 屬性。
限制
擴充後客戶端中客戶端層級方法的使用
客戶端層級的方法不一定存在於擴充後的客戶端上。對於這些客戶端,您需要先檢查其是否存在後再使用。
const xPrisma = new PrismaClient().$extends(...);
if (xPrisma.$connect) {
xPrisma.$connect()
}
巢狀操作的使用
query 擴充型別不支援巢狀讀取和寫入操作。