Null 和 Undefined
在 Prisma ORM 5.20.0 之前的版本,undefined
會被視為特殊值,不會包含在產生的查詢中。這種行為可能會導致非預期的結果和資料遺失。如果您使用的是舊版本的 Prisma ORM,我們強烈建議更新到 5.20.0 或更高版本,以利用新的 strictUndefinedChecks
功能。
如需舊版行為的文件,請參閱舊版行為。
嚴格 undefined 檢查 (預覽功能)
Prisma ORM 5.20.0 引入了一項新的預覽功能,稱為 strictUndefinedChecks
。此功能變更了 Prisma Client 處理 undefined
值的方式,能更有效地防止意外的資料遺失或非預期的查詢行為。
啟用嚴格 undefined 檢查
若要啟用此功能,請將以下內容新增至您的 Prisma schema
generator client {
provider = "prisma-client-js"
previewFeatures = ["strictUndefinedChecks"]
}
使用嚴格 undefined 檢查
當此功能啟用時
- 在查詢中明確將欄位設定為
undefined
會導致執行階段錯誤。 - 若要在查詢中略過欄位,請使用新的
Prisma.skip
符號,而不是undefined
。
範例用法
// This will throw an error
prisma.user.create({
data: {
name: 'Alice',
email: undefined // Error: Cannot explicitly use undefined here
}
})
// Use `Prisma.skip` (a symbol provided by Prisma) to omit a field
prisma.user.create({
data: {
name: 'Alice',
email: Prisma.skip // This field will be omitted from the query
}
})
此變更有助於防止意外刪除或更新,例如:
// Before: This would delete all users
prisma.user.deleteMany({
where: {
id: undefined
}
})
// After: This will throw an error
Invalid \`prisma.user.deleteMany()\` invocation in
/client/tests/functional/strictUndefinedChecks/test.ts:0:0
XX })
XX
XX test('throws on undefined input field', async () => {
→ XX const result = prisma.user.deleteMany({
where: {
id: undefined
~~~~~~~~~
}
})
Invalid value for argument \`where\`: explicitly \`undefined\` values are not allowed."
遷移路徑
若要遷移現有程式碼
// Before
let optionalEmail: string | undefined
prisma.user.create({
data: {
name: 'Alice',
email: optionalEmail
}
})
// After
prisma.user.create({
data: {
name: 'Alice',
email: optionalEmail ?? Prisma.skip
}
})
此新行為預計將在 Prisma ORM 6 中成為預設行為。
exactOptionalPropertyTypes
除了 strictUndefinedChecks
之外,我們也建議啟用 TypeScript 編譯器選項 exactOptionalPropertyTypes
。此選項強制要求選用屬性必須完全匹配,這有助於在您的程式碼中捕捉到 undefined
值的潛在問題。雖然 strictUndefinedChecks
會針對無效的 undefined
用法引發執行階段錯誤,但 exactOptionalPropertyTypes
會在建置過程中捕捉到這些問題。
在 TypeScript 文件中了解更多關於 exactOptionalPropertyTypes
的資訊。
意見回饋
與往常一樣,我們歡迎您對此功能提供意見回饋。請在 此預覽功能的 GitHub 討論區中分享您的想法和建議。
舊版行為
如果您使用的是 Prisma ORM 5.19.1 或更早版本,則本節與您相關。
Prisma Client 區分 null
和 undefined
null
是一個值undefined
意指不執行任何動作
在 Prisma ORM 與 GraphQL 內容中,null
和 undefined
可以互換,因此特別需要考量這一點。
以下資料代表 User
表格。以下範例都將使用這組資料
id | name | |
---|---|---|
1 | Nikolas | nikolas@gmail.com |
2 | Martin | martin@gmail.com |
3 | empty | sabin@gmail.com |
4 | Tyler | tyler@gmail.com |
影響多個記錄的查詢中的 null
和 undefined
本節將涵蓋 undefined
和 null
值如何影響與資料庫中多個記錄互動或建立多個記錄的查詢行為。
Null
考量以下 Prisma Client 查詢,它會搜尋所有 name
值與提供的 null
值相符的使用者
const users = await prisma.user.findMany({
where: {
name: null,
},
})
[
{
"id": 3,
"name": null,
"email": "sabin@gmail.com"
}
]
由於 null
作為 name
欄位的篩選條件,Prisma Client 將產生一個查詢,搜尋 User
表格中 name
欄位為空的所有記錄。
Undefined
現在考量在 name
欄位上使用 undefined
作為篩選值執行相同查詢的情況
const users = await prisma.user.findMany({
where: {
name: undefined,
},
})
[
{
"id": 1,
"name": "Nikolas",
"email": "nikolas@gmail.com"
},
{
"id": 2,
"name": "Martin",
"email": "martin@gmail.com"
},
{
"id": 3,
"name": null,
"email": "sabin@gmail.com"
},
{
"id": 4,
"name": "Tyler",
"email": "tyler@gmail.com"
}
]
在篩選器中使用 undefined
值,基本上是告訴 Prisma Client 您已決定不為該欄位定義篩選器。
寫入上述查詢的等效方式為
const users = await prisma.user.findMany()
此查詢將選取 User
表格中的每個資料列。
注意:在 Prisma Client 查詢的參數物件中使用 undefined
作為任何鍵的值,將導致 Prisma ORM 的行為如同未提供該鍵一樣。
雖然本節的範例著重於 findMany
函數,但相同的概念適用於任何可能影響多個記錄的函數,例如 updateMany
和 deleteMany
。
影響單一記錄的查詢中的 null
和 undefined
本節將涵蓋 undefined
和 null
值如何影響與資料庫中單一記錄互動或建立單一記錄的查詢行為。
注意:null
不是 findUnique()
查詢中的有效篩選值。
在影響單一記錄的查詢的篩選條件中使用 null
和 undefined
時,查詢行為與前一節中描述的行為非常相似。
Null
考量以下查詢,其中 null
用於篩選 name
欄位
const user = await prisma.user.findFirst({
where: {
name: null,
},
})
[
{
"id": 3,
"name": null,
"email": "sabin@gmail.com"
}
]
由於 null
用作 name
欄位的篩選器,Prisma Client 將產生一個查詢,搜尋 User
表格中 name
值為空的第一個記錄。
Undefined
如果 undefined
用作 name
欄位的篩選值,查詢的行為將如同完全沒有將篩選條件傳遞給該欄位一樣。
考量以下查詢
const user = await prisma.user.findFirst({
where: {
name: undefined,
},
})
[
{
"id": 1,
"name": "Nikolas",
"email": "nikolas@gmail.com"
}
]
在這種情況下,查詢將傳回資料庫中的第一個記錄。
代表上述查詢的另一種方式為
const user = await prisma.user.findFirst()
雖然本節的範例著重於 findFirst
函數,但相同的概念適用於任何影響單一記錄的函數。
GraphQL 解析器中的 null
和 undefined
對於此範例,請考量基於以下 Prisma schema 的資料庫
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
}
在以下更新使用者的 GraphQL 變異中,authorEmail
和 name
都接受 null
。從 GraphQL 的角度來看,這表示欄位是選用的
type Mutation {
// Update author's email or name, or both - or neither!
updateUser(id: Int!, authorEmail: String, authorName: String): User!
}
但是,如果您將 authorEmail
或 authorName
的 null
值傳遞給 Prisma Client,則會發生以下情況
- 如果
args.authorEmail
為null
,則查詢將失敗。email
不接受null
。 - 如果
args.authorName
為null
,Prisma Client 會將name
的值變更為null
。這可能不是您希望更新的運作方式。
updateUser: (parent, args, ctx: Context) => {
return ctx.prisma.user.update({
where: { id: Number(args.id) },
data: {
email: args.authorEmail, // email cannot be null
name: args.authorName // name set to null - potentially unwanted behavior
},
})
},
請改為在輸入值為 null
時,將 email
和 name
的值設定為 undefined
。這樣做與完全不更新欄位相同
updateUser: (parent, args, ctx: Context) => {
return ctx.prisma.user.update({
where: { id: Number(args.id) },
data: {
email: args.authorEmail != null ? args.authorEmail : undefined, // If null, do nothing
name: args.authorName != null ? args.authorName : undefined // If null, do nothing
},
})
},
null
和 undefined
對條件的影響
使用條件篩選時,有一些注意事項可能會產生非預期的結果。當使用條件篩選時,您可能會預期一個結果,但根據 Prisma Client 處理可為 null 值的方式,您可能會收到另一個結果。
下表提供不同運算子如何處理 0、1 和 n
個篩選器的高階概觀。
運算子 | 0 個篩選器 | 1 個篩選器 | n 個篩選器 |
---|---|---|---|
OR | 傳回空列表 | 驗證單一篩選器 | 驗證所有篩選器 |
AND | 傳回所有項目 | 驗證單一篩選器 | 驗證所有篩選器 |
NOT | 傳回所有項目 | 驗證單一篩選器 | 驗證所有篩選器 |
此範例示範 undefined
參數如何影響使用 OR
運算子之查詢傳回的結果。
interface FormData {
name: string
email?: string
}
const formData: FormData = {
name: 'Emelie',
}
const users = await prisma.user.findMany({
where: {
OR: [
{
email: {
contains: formData.email,
},
},
],
},
})
// returns: []
查詢從 formData 物件接收篩選器,其中包含選用的 email 屬性。在此情況下,email 屬性的值為 undefined
。當執行此查詢時,不會傳回任何資料。
這與 AND
和 NOT
運算子形成對比,如果您傳入 undefined
值,這兩個運算子都會傳回所有使用者。
這是因為將
undefined
值傳遞給AND
或NOT
運算子,與完全不傳遞任何值相同,這表示範例中的findMany
查詢將在沒有任何篩選器的情況下執行,並傳回所有使用者。
interface FormData {
name: string
email?: string
}
const formData: FormData = {
name: 'Emelie',
}
const users = await prisma.user.findMany({
where: {
AND: [
{
email: {
contains: formData.email,
},
},
],
},
})
// returns: { id: 1, email: 'ems@boop.com', name: 'Emelie' }
const users = await prisma.user.findMany({
where: {
NOT: [
{
email: {
contains: formData.email,
},
},
],
},
})
// returns: { id: 1, email: 'ems@boop.com', name: 'Emelie' }