🫥

GraphQL の配列が nullable になって扱いにくい問題の対処法

2023/06/05に公開

GraphQLのスキーマに配列が含まれるとGraphQL Code Generatorの生成された型でnullableになってしまい、フロントエンドで扱いにくい問題の対処方法を解説します。

対象読者

  • 私も含めたGraphQL初心者
  • GraphQL Code Generatorなどで型生成をしている

背景

今までRESTしかやったことがなかったのですが、最近GraphQLにチャレンジしています。GraphQL Code Generatorで型生成をしているのですが、配列がnullableになってしまい、フロントエンドで扱いにくい問題が発生しました。

例えば次のようなブログのスキーマを想定してみます。

type BlogPost {
  slug: string!
}

query Query {
  allBlogPost: [BlogPost]
}

このスキーマをGraphQL Code Generatorで型生成するとこのような型が生成されます。

export type Maybe<T> = T | null;

export type Query = {
  __typename?: "Query";
  allBlogPost?: Maybe<Array<Maybe<BlogPost>>>;
};

allBlogPostBlogPost の配列 (空配列を含む) のつもりだったのですが、実際は次の5パターンが返ってくる可能性のあるスキーマになってしまいました。パターンが増えるので考えることが増えてしまったり、多彩なパターンのテストを書かなければならず開発効率が低下してしまいます。

REST脳で意外に思ったのが、配列の中にnullが入るパターンです。 GraphQLスキーマではエクスクラメーションマークを明示的に付けない場合はnullableになるのでこのような型になるようです。

  • {}
  • {allBlogPost: null}
  • {allBlogPost: []}
  • {allBlogPost: [null]}
  • {allBlogPost: [{slug: "slug"}]}

これでslug一覧を生成しようとおもったらこのような感じでしょうか..。

data?.allBlogPost
  ?.filter((v): v is NonNullable<typeof v> => v !== null)
  .map((v) => v.slug) ?? [];

null を許容しないスキーマに変更する

GraphQLのドキュメントを参考に、エクスクラメーションマークを付けてnon-nullableにしてみました。配列の内外共にエクスクラメーションマークを付けるようにします。

https://graphql.org/learn/schema/

query Query {
  allBlogPost: [BlogPost!]!
}

このスキーマをGraphQL Code Generatorで型生成するとこのような型が生成されます。

export type Query = {
  __typename?: "Query";
  allBlogPost: <Array<BlogPost>>;
};

パターンが絞られ、必ず配列が返るようになります。

  • {allBlogPost: []}
  • {allBlogPost: [{slug: "slug"}]}

これならシンプルに実装できます。

data.allBlogPost.map((v) => v.slug);

まとめ

明示的にnon-nullableを指示することで、実装をシンプルにできました。バックエンド側でもnullを返さないよう実装するよう仕様が明確になります。

Discussion