GraphQL の配列が nullable になって扱いにくい問題の対処法
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>>>;
};
allBlogPost
は BlogPost
の配列 (空配列を含む) のつもりだったのですが、実際は次の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にしてみました。配列の内外共にエクスクラメーションマークを付けるようにします。
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