Open8

GraphQLの基本的なプラクティス

adwdadwd

GraphQLはただ導入しただけだとメリットが享受できずRESTでよかったかもというケースをいくつか見てきていて、公式ドキュメントに書いてあるレベルのプラクティス(書いてたら私見も混じってきた😂)を一つずつ理解して実行していけばGraphQLのメリットを実感できるようになるはず。プラクティス一つ一つの詳細はここでは触れないが思いつくものを並べていく。

adwdadwd

GraphQLスキーマはビジネスロジックを表現する

GraphQLスキーマの設計が多分一番重要で経験が必要なところだと思っていて、でもすぐにベストな形にするのは難しいと思う。GitHubやShopifyなどGraphQLを大規模な基盤で数年以上運用している会社が記事や動画でGraphQLスキーマ設計に関する知見を公開してくれていて、それを見ると参考になる。

https://www.youtube.com/watch?v=pJamhW2xPYw

https://github.com/Shopify/graphql-design-tutorial/blob/master/lang/TUTORIAL_JAPANESE.md

https://principledgraphql.com/agility#4-abstract-demand-oriented-schema

adwdadwd

Fragment Colocation

Fragment Colocationはコンポーネントが要求するデータをフラグメントを使ってReactのコンポーネントツリーとデータ要求ツリーを一致させることで、子孫コンポーネントが要求するデータを抽象化できることがメリットだと思う。

https://relay.dev/docs/principles-and-architecture/thinking-in-relay/#specifying-the-data-requirements-of-a-component

例えば、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 に含めておけばいいだけで、子孫のコンポーネントに関する知識を持たなくて済む。

adwdadwd

Query, Fragmentに再利用性はない

これに関連して考えると、QueryやFragmentに再利用性はなくなると思う。Fragmentはあるコンポーネントが要求するデータを記述するものであるので、それを使い回すことはない的な。だけど、コンポーネントは再利用性はある。Fragmentが要求するデータを供給できるクエリに組み込めばどこでもFragmentを使うことができ、そのフラグメントに紐づくコンポーネントが表示できる。
なので、フラグメントの命名規則に関してはRelayのスタイルを採用すると考えることがなくて良いと思う。

https://relay.dev/docs/guided-tour/rendering/fragments/

adwdadwd

ディレクティブを活用してスキーマに多くのロジックをもたせる

adwdadwd

dataloaderなどを使ってN+1が起きないようにする

adwdadwd

Schema stitchingやApollo Federation、GraphQL Meshでアグリゲーションを省力化する

adwdadwd

id の設計はグローバルで一意か、そのTypeで一意なものにする

クライアントサイドでのキャッシュのために id が重要で、Apolloではデフォルトで __typename + id がキャッシュのキーとなり、Relay ID Specでは id 単独でグローバルでユニークになる