語義可空性
實驗性:由於嚴格語義可空性仍在變化中,Relay 內的實作和行為可能會在我們更了解這個概念及其含義時發生變化,並且可能會有意料之外的行為。
動機
GraphQL 的優勢之一是其欄位級別的錯誤處理,可以大幅提高回應的彈性。然而,如今的錯誤處理取決於欄位可空性,這也是為什麼建議將所有欄位預設為可空欄位的 最佳實務。這產生了一種權衡,即啟用最大彈性意味著用戶端開發人員必須在其元件內手動處理所有可能的欄位可空性排列。@required
可以提供一些幫助,但最終只是一種非常粗糙的工具。
建議的解決方案
語義可空性 是一個早期的 GraphQL 規格提案,旨在將 GraphQL 規格中的錯誤處理和可空性分離,以實現最大的彈性,同時仍將欄位的「語義可空性」(伺服器上實際解析器函數/方法的可空性)暴露給用戶端。
該提案的運作方式是允許 Schema 指定一種新的可空性類型,即「僅在發生錯誤時為空」。如果用戶端看到此類型,並且 用戶端具有某種處理頻外欄位錯誤的策略,則它可能會將暴露給使用者程式碼的欄位視為不可空。
完整的規格變更可能需要向 GraphQL 的 Schema 定義語言新增額外的語法,但同時,各種 GraphQL 伺服器和用戶端已就一個臨時指令 @semanticNonNull
進行協作,可用於實驗這個概念。
簡而言之,您可以在 Schema 中的欄位新增 @semanticNonNull
,以表明該欄位在語義上是不可空的,但用戶端仍應準備好處理錯誤。
如果您透過新增 @throwOnFieldError
指令,為查詢或片段啟用用戶端錯誤處理,Relay 將在您的 Schema 中尋找 @semanticNonNull
指令,並為這些欄位產生不可空的 Flow/TypeScript 型別。
例如,如果您的伺服器永遠不會為使用者名稱傳回 null
,除非發生錯誤,因為其解析器的型別設定為不可空,則您可以在 Schema 中將 @semanticNonNull
套用到該欄位。
directive @semanticNonNull(levels: [Int] = [0]) on FIELD_DEFINITION
type User {
name: String @semanticNonNull
}
將指令新增到 Schema 後,您可以將 @throwOnFieldError
新增到您的片段和查詢,以表明如果讀取片段時遇到任何欄位錯誤,用戶端應擲回錯誤。
請務必在您的應用程式中,將 React 錯誤邊界 新增到任何使用 @throwOnFieldError
的元件上方。
在以下範例中,Relay 為 user.name
產生的 TypeScript 或 Flow 型別將會是不可空的。
如果 Relay 收到 user.name
的欄位錯誤,useFragment
將擲回錯誤。因此,務必確保您已設置足夠的 React 錯誤邊界來捕捉這些錯誤。
import type {UserComponent_user$key} from 'UserComponent_user.graphql';
const React = require('React');
const {graphql, useFragment} = require('react-relay');
type Props = {
user: UserComponent_user$key,
};
function UserComponent(props: Props) {
const user = useFragment(
graphql`
fragment UserComponent_user on User @throwOnFieldError {
name # Will be typed as non-nullable
}
`,
props.user,
);
return <div>{user.name}</div>
}
範例說明
如需實務範例,請參閱此範例專案,其中顯示 Relay 設定為搭配 Grats 使用 @semanticNonNull
和 @throwOnFieldError
,而 Grats 具有支援,可以自動衍生包含實驗性 @semanticNonNull
指令的 Schema。