本文へスキップ
バージョン: v18.0.0

セマンティック・ヌラビリティ

危険

実験的機能: 厳密なセマンティック・ヌラビリティはまだ開発中であるため、Relayにおける実装と動作は変更される可能性があり、この概念とその影響についてさらに理解を深めるにつれて予期せぬ動作が発生する可能性があります。

動機

GraphQLの強みの1つは、フィールド単位のエラー処理が可能で、レスポンスの堅牢性を大幅に向上できることです。しかし、現在のエラー処理はフィールドのヌラビリティに依存しており、そのためベストプラクティスとして推奨されているように、すべてのフィールドをデフォルトでヌラブルにすることが重要です。これにより、最大の堅牢性を確保するには、クライアント開発者がコンポーネント内でフィールドのヌラビリティのあらゆる組み合わせを手動で処理する必要があるというトレードオフが生じます。@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を追加して、フラグメントが読み取られる際にフィールドエラーが発生した場合にクライアントがエラーをスローすることを示します。

注意

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>
}

実践的な例として、この例題プロジェクトを参照してください。このプロジェクトでは、Gratsとともに@semanticNonNull@throwOnFieldErrorを使用するようにRelayが設定されています。Gratsは実験的な@semanticNonNullディレクティブを含むスキーマを自動的に導出する機能を備えています。

参考資料