跳到主要內容

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 檢查

當此功能啟用時

  1. 在查詢中明確將欄位設定為 undefined 會導致執行階段錯誤。
  2. 若要在查詢中略過欄位,請使用新的 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 區分 nullundefined

  • null 是一個
  • undefined 意指不執行任何動作
資訊

以下資料代表 User 表格。以下範例都將使用這組資料

影響多個記錄的查詢中的 nullundefined

本節將涵蓋 undefinednull 值如何影響與資料庫中多個記錄互動或建立多個記錄的查詢行為。

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 函數,但相同的概念適用於任何可能影響多個記錄的函數,例如 updateManydeleteMany

影響單一記錄的查詢中的 nullundefined

本節將涵蓋 undefinednull 值如何影響與資料庫中單一記錄互動或建立單一記錄的查詢行為。

警告

注意null 不是 findUnique() 查詢中的有效篩選值。

在影響單一記錄的查詢的篩選條件中使用 nullundefined 時,查詢行為與前一節中描述的行為非常相似。

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 解析器中的 nullundefined

對於此範例,請考量基於以下 Prisma schema 的資料庫

model User {
id Int @id @default(autoincrement())
email String @unique
name String?
}

在以下更新使用者的 GraphQL 變異中,authorEmailname 都接受 null。從 GraphQL 的角度來看,這表示欄位是選用的

type Mutation {
// Update author's email or name, or both - or neither!
updateUser(id: Int!, authorEmail: String, authorName: String): User!
}

但是,如果您將 authorEmailauthorNamenull 值傳遞給 Prisma Client,則會發生以下情況

  • 如果 args.authorEmailnull,則查詢將失敗email 不接受 null
  • 如果 args.authorNamenull,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 時,將 emailname 的值設定為 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
},
})
},

nullundefined 對條件的影響

使用條件篩選時,有一些注意事項可能會產生非預期的結果。當使用條件篩選時,您可能會預期一個結果,但根據 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。當執行此查詢時,不會傳回任何資料。

這與 ANDNOT 運算子形成對比,如果您傳入 undefined 值,這兩個運算子都會傳回所有使用者。

這是因為將 undefined 值傳遞給 ANDNOT 運算子,與完全不傳遞任何值相同,這表示範例中的 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' }