GraphQLの基本的なプラクティス
GraphQLはただ導入しただけだとメリットが享受できずRESTでよかったかもというケースをいくつか見てきていて、公式ドキュメントに書いてあるレベルのプラクティス(書いてたら私見も混じってきた😂)を一つずつ理解して実行していけばGraphQLのメリットを実感できるようになるはず。プラクティス一つ一つの詳細はここでは触れないが思いつくものを並べていく。
GraphQLスキーマはビジネスロジックを表現する
GraphQLスキーマの設計が多分一番重要で経験が必要なところだと思っていて、でもすぐにベストな形にするのは難しいと思う。GitHubやShopifyなどGraphQLを大規模な基盤で数年以上運用している会社が記事や動画でGraphQLスキーマ設計に関する知見を公開してくれていて、それを見ると参考になる。
Fragment Colocation
Fragment Colocationはコンポーネントが要求するデータをフラグメントを使ってReactのコンポーネントツリーとデータ要求ツリーを一致させることで、子孫コンポーネントが要求するデータを抽象化できることがメリットだと思う。
例えば、Zennのトップページがこういうコンポーネントツリーがだったとして、
const ZennTopPage = () => {
const { data, loading, error } = useQuery(ZennTopPageQuery)
return (
<ZennLayout>
<Header />
<TechArticles />
{/* ... */}
</ZennLayout>
)
}
こんなクエリでデータが取得できたとする。
query ZennTopPageQuery {
techArticles {
iconUrl
title
updatedAt
likeCount
author {
userName
iconUrl
}
}
# ...
}
ここで TechArticles
コンポーネントの中で TechArticleItem
をこういうふうに使ってるとする。
const TechArticles = props => (
<Container>
{props.techArticles.map((item) => <TechArticleItem item={item} />)}
</Container>
)
この場合、 TechArticleItem
コンポーネントの表示に必要なデータをその親の親である ZennTopPage
が知っていることになってしまい、凝集度が下がってしまっている。ここでFragmentを使って、コンポーネントツリーとQueryのツリーを一致させる。
query ZennTopPageQuery {
...TechArticlesFragment
# ...
}
fragment TechArticlesFragment on Query {
techArticles {
...TechArticleItemFragment
}
}
fragment TechArticleItemFragment on TechArticle {
iconUrl
title
updatedAt
likeCount
author {
userName
iconUrl
}
}
これで TechArticles
コンポーネントは中身は知らないけど TechArticles
コンポーネントが要求するデータがまとまっている TechArticlesFragment
フラグメントを自分の ZennTopPageQuery
に含めておけばいいだけで、子孫のコンポーネントに関する知識を持たなくて済む。
Query, Fragmentに再利用性はない
これに関連して考えると、QueryやFragmentに再利用性はなくなると思う。Fragmentはあるコンポーネントが要求するデータを記述するものであるので、それを使い回すことはない的な。だけど、コンポーネントは再利用性はある。Fragmentが要求するデータを供給できるクエリに組み込めばどこでもFragmentを使うことができ、そのフラグメントに紐づくコンポーネントが表示できる。
なので、フラグメントの命名規則に関してはRelayのスタイルを採用すると考えることがなくて良いと思う。
ディレクティブを活用してスキーマに多くのロジックをもたせる
dataloaderなどを使ってN+1が起きないようにする
Schema stitchingやApollo Federation、GraphQL Meshでアグリゲーションを省力化する
id
の設計はグローバルで一意か、そのTypeで一意なものにする
クライアントサイドでのキャッシュのために id
が重要で、Apolloではデフォルトで __typename + id
がキャッシュのキーとなり、Relay ID Specでは id
単独でグローバルでユニークになる