GraphQL の fragment による query 組み立て
GraphQL では、画面上の各構成要素は、表示するために何が必要か fragment として宣言し、親が子の fragment を束ね、基本的にはルートとなるコンポーネントが query として組み立てて API 呼び出しをします。
よく語られていることではありますが、とはいえ具体例があったほうがイメージがつきやすいと思うので、Zenn の Articles ページ (https://zenn.dev/articles/explore) を題材に GraphQL fragment をどう使うかを説明します。
※想像で書いています。Zenn は実際には GraphQL を使っていなそうです。
Zenn の Articles ページを構成する要素たち
上記のページがどのような要素で作られているかを簡単に示すと、以下のようになるでしょう。
- ArticlesPage
- FeaturedArticles
- ArticleCard
- ArticleCard
- ArticleCard
- ...
- TopTechArticles
- ArticleItem
- ArticleItem
- ...
- FeaturedArticles
※ナビゲーションの表示や、ログイン状況の表示などは一旦割愛します
ここでいう「要素」とは、実際には React の component かもしれませんし、Web Components だったり、その他フレームワークを使った場合は何か別のものかもしれません。何を使っても、あるページについて画面を整理すると木構造となっているはずなので、それらの要素に名前をつけていきます。
ArticlesPage はこのページ全体を指します。
その中には、FeaturedArticles, TopTechArticles, TopIdeaArticles など、いくつかの記事の集まりを特集したものがあります。
さらに、その中には個別の記事があります。
Featured の記事たちはカード状になっています。これを ArticleCard とします。
その下の Top Tech Articles はカード状に囲うようにはなっていないようです。これを ArticleItem とします。
ArticleCard
まずは ArticleCard について、何が表示されているか書き出してみます。
この、ArticleCard を表示するために必要なデータが、そのまま fragment になります。
Zenn の GraphQL schema についての具体的な考察はここでは省略しますが、おそらく次のような fragment を ArticleCard 用に書くことになります。
fragment ArticleCard on Article {
title
iconUrl
category {
name
}
author {
name
avatarUrl
}
createdAt
minutesToRead
likes {
totalCount
}
}
ArticleItem
次に、ArticleItem についてです。こちらはカテゴリが不要なようですね。
同様に整理すると次のような fragment になりそうです。
fragment ArticleItem on Article {
title
iconUrl
author {
name
avatarUrl
}
createdAt
minutesToRead
likes {
totalCount
}
}
FeaturedArticles
最下層の ArticleCard および ArticleItem の fragment について考えたので、次はその上の FeaturedArticles について考えます。
これは、おそらく Featured
という固定文言を表示し、ArticleCard を 6 つ並べればよさそうです。
Zenn の GraphQL schema を考えると、Root Query に featuredArticles という形で問い合わせることになりそうなので、on Query
な fragment になります。
このとき大事なのは、FeaturedArticles という要素が何を知っていて何を知らないか、ということです。子として ArticleCard を 6 つ並べることは知っていそうですが、その ArticleCard が具体的に何を表示しているかは知らなそうです。
fragment FeaturedArticles on Query {
featuredArticles(first: 6) {
...ArticleCard
}
}
TopTechArticles
TopTechArticles についてもほぼ同様に、Top Tech Articles
という固定文言の下に 16 の記事を ArticleItem として並べればよさそうです。
fragment TopTechArticles on Query {
topTechArticles(first: 16) {
...ArticleItem
}
}
ArticlesPage
最後に、最上位の ArticlesPage です。
ここでは、一番上に Articles という固定文言とアイコン、そして FeaturedArticles や TopTechArticles 他を並べればよさそうです。
ここでもまた親が子の fragment を束ねることになります。
重要なのは、子の fragment の面倒は見るが孫の面倒は見ないということです。
なぜなら、孫としてどういう存在がいるかは親からは隠蔽されており、その孫の面倒は子が見るからです。
この要素が最上位になるため、ここでは fragment ではなく query として実際に API を叩くことになるでしょう。
query ArticlesPage {
...FeaturedArticles
...TopTechArticles
}
まとめ
GraphQL では、画面上の各構成要素は、表示するために何が必要か fragment として宣言し、親が子の fragment を束ね、基本的にはルートとなるコンポーネントが query として組み立てて API 呼び出しをします。
今回は簡単化のためにすべて Article という GraphQL Object の下でいろいろ取れることとしましたが、実際には 1 コンポーネントで複数のリソースにアクセスしたくなり、複数の fragment を持つことも多いでしょう。
そのような場合には、私は ArticleItem_Article
というような「要素名 + GraphQL Object 名」という形で整理するといいんじゃないかなと考えています。
自明かもしれませんが、「fragment は各要素に紐付き整理される」ということであって、「すべての要素は fragment を持つ」ということではないことに注意してください。ArticleCard はより、汎用的な Card だったり Avatar のような汎用的な要素に分割できる可能性があります。その際、Card や Avatar は特定の GraphQL Object に紐付かないため、fragment の宣言をすることはできないですし、する必要もありません。
Discussion