Upgrade to Pro — share decks privately, control downloads, hide ads and more …

GraphQL の onError と Nullability のこれから

GraphQL の onError と Nullability のこれから

Avatar for Yosuke Kurami

Yosuke Kurami

October 22, 2026

More Decks by Yosuke Kurami

Other Decks in Programming

Transcript

  1. About Me • ID(GitHub, X): @Quramy • Query と空目しやすいのが悩み •

    GraphQL と僕: • GraphQL Tokyo Organizer • 最近初めて Hasura を触りました
  2. Null Propagation • 右のスキーマを例として考える (本の検索システムのようなイメージ) • データモデリング上、本には必ず著者が紐づく (Book has an

    author の関係) type Book { id: ID! title: String! author: Author! } type Author { id: ID! name: String } type Query { book(id: ID!): Book }
  3. Null Propagation • クエリ実行時に次のエラーが発生したとする 「本は引けたが、著者を引くのに失敗した」 • 著者(Author) と本(Book) は分散して保存されていると 起きうる問題

    query BookQuery($bookId: ID) { book (id: $bookId) { id title author { id name } } } type Book { id: ID! title: String! author: Author! } type Author { id: ID! name: String } type Query { book(id: ID!): Book }
  4. Null Propagation • クエリ実行時に次のエラーが発生したとする 「本は引けたが、著者を引くのに失敗した」 • Book.author は必須フィールド ! であるため、

    「スキーマを満たす Book 型を出力できない」となる • Null を許容できるフィールドとなるまで型階層を遡上 (今回の例では Query.book が Null 許容) { "data": { "query": { "book": null } } } type Book { id: ID! title: String! author: Author! } type Author { id: ID! name: String } type Query { book(id: ID!): Book }
  5. Null Propagation • 「Null を許容できるフィールドとなるまで型改装を遡上し、親を Null としていく」 • この挙動は GraphQL

    の仕様が定めている挙動 https://spec.graphql.org/September2025/#sec-Handling-Execution-Errors Since Non-Null response positions cannot be null, execution errors are propagated to be handled by the parent response position. If the parent response position may be null then it resolves to null, otherwise if it is a Non-Null type, the execution error is further propagated to its parent response position.
  6. Null Propagation の問題点 • 本来取得できていたフィールドが巻き込まれてしまう • 先ほどの例(P.5)では id, title は問題ないにも関わらず、

    author の問題に巻き添えを食らう形となる • スキーマ設計で Book.author を Nullable とすれば伝播は起き ない • 「フィールドは Nullable にする」がベストプラクティス扱いに https://graphql.org/learn/schema-design/#nullability Good: type Book { id: ID! title: String! author: Author } Bad: type Book { id: ID! title: String! author: Author! }
  7. Null Propagation の問題点 • 「フィールドは Nullable がベストプラクティス」はサーバーサイドの堅牢性という意味では 正しいが、別の問題が生じる • クライアントが

    Non-null Assertion を引き受けねばならない • TypeScript で書くと、 type Query = { book?: { author?: { id: string } } } • 正常系の範囲では Non-Null なフィールドであっても逐一検査が必要 • 逆に意味的にも Nullable なフィールドの場合も、 null という結果から「何故このフィー ルドが null なのか」を検証するのが手間 (レスポンスの errors フィールドを見る必要がある)
  8. Nullability WG • Nullability の問題点を解消するため、Nullability WG が発足 (検討当初は Client Controlled

    Nullability WG という名前) • 2026年2月で方針が合意されたため解散 • 前身である Client Controlled Nullability を含めると、問題の解決のために様々な仕様が検 討された • https://github.com/graphql/graphql-wg/blob/main/rfcs/SemanticNullability.md
  9. 最終合意案: onError • Nullability WG は「Null Propagation をやめる」という方針で解散された • 方法として提案されているのが、Execution

    への onError オプション追加 ※ 2026.10現在 RFC 1(提案段階)。今すぐ使えるものではないことに留意 https://github.com/graphql/graphql-spec/pull/1163 enum OnError { "PROPAGATE", "NULL", "HALT", } declare function execute(args: { schema: Schema, document: Document, Value Semantics PROPAGATE (default) フィールドエラーは親を遡り Null Propagation させる。 NULL フィールドの ! の有無に関わらず、当該フィールドは Null と なる。Null Propagation は起きない。 HALT レスポンスの生成を即時中断する。 { ”data”: null, “errors”:[…] } な応答となる。 onError?: OnError, }): ExecutingResult
  10. onError によって何が変わるのか • onError: “NULL” なクライアントにとってはスキーマの ! の意味が異 なる •

    e.g. Book.author は値を持つが Error 発生時は Null となりうる • いわば、Nullable と Non-Null の中間的な位置付け。 Semantic Non-Null (後述) • 「分散システムのエラーを回避するために Null 許容」という従来のベ ストプラクティスは、onError: NULL を前提とする系では陳腐化。 業務上 Null にならないのであれば ! をつけて良い。 type Book { id: ID! title: String! author: Author! } type Author { id: ID! name: String } type Query { book(id: ID!): Book }
  11. Semantic Non-Nullability • Semantic Non-Nullability, すなわち「業務上 Null ではない」という性質は WG でも長く議

    論されていた (案としては ! 以外の型の導入などが挙がっていた) • エコシステム発で提案された仕組みが @semanticNonNull ディレクティブ Apollo や Relay で対応実装がなされている (ディレクティブのみなので Spec 依存がない) type Book { id: ID! title: String! author: Author @semantiNonNull } • Jordan Eldredge氏の GraphQL Conf での説明動画: https://www.youtube.com/watch?v=kVYlplb1gKk
  12. @semanticNonNull と onError の関係 • onError: NULL は Semantic Non-Null

    を実現する別の形の提案 • @semanticNonNull: スキーマ上 で Required / Null / Semantic Null の3値を表現する • onError: スキーマのみでは Semantic Non null かどうかは不明。 onError: NULL を使うクライアントに限り ! が Semantic Non Null の意味に • どちらの方法も「業務上エラーにならないフィールドを Required なものとして扱いつつ、 部分的なエラーをクライアントでハンドリングする」を達成可能
  13. @semanticNonNull と onError の関係 • onError(= リクエストパラメータで表現) v.s. @semanticNonNull(= スキーマで表現)

    • リクエストパラメータ側に明確な優位性があるわけではない 少なくとも、自分が WG の議論を追いかけた範囲ではそれに類するものは見つからず • (IMO) スキーマディレクティブかどうかはともかく、スキーマ上で 3値表現となる世界の 方が、実行時のパラメータに依存するよりは良いと考えている • とはいえ、onError: NULL が使えるようになれば @semanticNonNull は不要となる。 @semanticNonNull は Semantic Nullability 過渡期の仕組みとして向き合うのが穏当
  14. まとめ • 現状の GraphQL 仕様はフィールドへの ! 付与は Null Propagation という問題を抱えている

    • onError: NULL の仕様が提案段階。onError: NULL の世界では ! が付与されたフィールドの 意味は “Semantic Non-Null” となる • 「Null Propagation を回避するためにフィールドを Nullable にする」思想は陳腐化する • 利用しているフレームワークが対応しているのであれば、段階的移行措置として @semanticNonNull ディレクティブを利用可能
  15. 参考資料 • onError の仕様案: https://github.com/graphql/graphql-spec/pull/1163 • Semantic Non-Null type について(棄却された案):

    https://quramy.medium.com/ graphql-%E3%81%AE-semantic-non-null-type-rfc%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6-49fb18a06afb • @semanticNonNull と Relay 実装の解説記事: https://quramy.medium.com/relay-v18- fi %E3%81%AE-throwon elderror-%E3%81%A8-graphql-nullability-55ce63c3aae2