跳到主要内容
版本: v18.0.0

语义非空

danger

实验性: 由于严格语义非空仍在发展中,Relay 中的实现和行为可能会发生变化,并且在我们了解更多关于该理念及其影响时,可能会出现意外行为。

动机

GraphQL 的优势之一是其字段级错误处理,这可以显著提高响应弹性。然而,如今这种错误处理依赖于字段非空性,这就是为什么推荐最佳实践是将所有字段默认设置为可空的。这造成了一个权衡,即为了获得最大的弹性,客户端开发人员必须在其组件中手动处理字段非空性的所有可能排列@required 可以提供一些帮助,但它最终是一个非常粗糙的工具。

拟议的解决方案

语义非空 是 GraphQL 规范的早期提案,旨在将错误处理和非空性在 GraphQL 规范中解耦,以实现最大的弹性,同时仍然将字段的“语义非空性”(服务器上实际解析函数/方法的非空性)暴露给客户端。

该提案通过允许模式指定一种新的非空性类型“仅在错误时为 null”来实现。如果客户端看到此类型,并且客户端有某种策略来处理带外字段错误,它可能会将暴露给用户代码的字段视为非空。

完整的规范更改可能需要在 GraphQL 的模式定义语言中添加额外的语法,但在此期间,各种 GraphQL 服务器和客户端已经合作开发了一个临时指令@semanticNonNull,可用于试验此想法。

简而言之,您可以在模式中的字段中添加 @semanticNonNull 来指示该字段在语义上是非空的,但客户端仍应准备好处理错误。

Relay 将在您的模式中查找 @semanticNonNull 指令,如果您通过添加@throwOnFieldError 指令为您的查询或片段启用客户端错误处理,它将为这些字段生成非空的 Flow/TypeScript 类型。

例如,如果您的服务器永远不会为用户的姓名返回 null,除非出现错误,因为它的解析器被类型化为非空,那么您可以在模式中的该字段上应用 @semanticNonNull

schema.graphql
directive @semanticNonNull(levels: [Int] = [0]) on FIELD_DEFINITION

type User {
name: String @semanticNonNull
}

在将指令添加到您的模式后,您可以将 @throwOnFieldError 添加到您的片段和查询中,以指示如果在读取片段时遇到任何字段错误,客户端应抛出错误。

note

请确保将 React 错误边界 添加到您的应用中,位于使用 @throwOnFieldError 的任何组件之上。

在下面的示例中,Relay 为 user.name 生成的 TypeScript 或 Flow 类型将是非空的。

caution

如果 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 配置为使用 @semanticNonNull@throwOnFieldError 以及 Grats,它 支持 自动推导出包含实验性 @semanticNonNull 指令的模式。

进一步阅读