LoginSignup
1
1

Relayはnodeクエリを発行することがあるらしい

Last updated at Posted at 2023-12-07

本記事のまとめ

  • Relay GraphQL Clientがnodeクエリを利用し、NodeResolverをコールするタイミングを明らかにした。
  • @refetchableを利用すると、relay-compilerが、nodeクエリを利用するようなクエリを生成することがある。
  • RefetchableFragmentを利用しない場合、nodeクエリと、NodeResvolerは不要になる(と思われる)。

背景

Nodeは、グローバルに一意なIDを持つインターフェースのことです。
以下のように、GraphQLクライアントがキャッシュやデータの再フェッチを行うために、GraphQLの各オブジェクトに実装しておくことが推奨されます。

interface Node {
  id: ID!
}

# Nodeを実装したオブジェクトの例
type MyObject implements Node {
  id: ID!
  name: String!
}

このことは、以下のページにも書かれています。

ただし、Nodeを実装しただけでは不十分で、以下のように、スキーマにnodeクエリを指定する必要があるようです1。また、クエリがあるということはResolverも当然実装する必要があります(サーバ側の話なので、ここではResolverの内部実装ついては触れません)。

type Query {
  node(id: ID!): Node
}

しかし、そこで1つ疑問が生じます。通常クライアント側に

query PageQuery {
  myObject {
    id
    name
  }
}

のようなコードは書くことがあっても、

query PageQuery($id: ID!) {
  node(id: $id) {
    name
  }
}

のように、直接nodeクエリを書くことは基本的にないのではないでしょうか。そこで、いつどのタイミングでこのnodeクエリが発行されるのか、もしくは実は発行されないのか、気になったため調べてみました。

調査したこと

まず、無邪気にRelayの質問ページから質問を投げてみました。

すると、以下のページを案内されました。

ページを確認したところ、refetchの時に使用されることはわかったのですが、正直確証を得るまでには至りませんでした。そこで、実際にサンプルを動かしてみることにしました。セットアップが手間なので、Next.jsを使ってサンプルを作ります。

まず、pages/index.tsxに、クエリ本体とRefetchableなクエリを用意します。事前にRelayとNext.jsのセットアップは済ませてあるものとします。

const query = graphql`
  query pagesIndexQuery {
    myObject {
      id
      name
      ...pagesIndexFragment
    }
  }
`

const fragment = graphql`
  fragment pagesIndexFragment on MyObject 
    @refetchable(queryName: "RefetchQuery") {
      id
      name
  }
`

ここで、pnpm buildを行い、自動生成されたRefetchQuery.graphql.tsを確認してみます。120行目あたりのparams.textを確認すると確かにnodeクエリが生成されていることが確認できました。

"query RefetchQuery(\n  $id: ID!\n) {\n  node(id: $id) {\n    __typename\n    ...pagesIndexFragment (以下省略)

続いて、コンポーネントやサーバも作成し、実際にNodeResolverが叩かれるかを確認してみます。先ほどの pages/index.tsx以下のように修正します。

import { graphql, useLazyLoadQuery, useRefetchableFragment } from "react-relay"
import { pagesIndexQuery } from "@/queries/__generated__/pagesIndexQuery.graphql"

const query = graphql`
  query pagesIndexQuery {
    myObject {
      id
      name
      ...pagesIndexFragment
    }
  }
`

const fragment = graphql`
  fragment pagesIndexFragment on MyObject @refetchable(queryName: "RefetchQuery") {
    id
    name
  }
`

export default function Index() {
  const data = useLazyLoadQuery<pagesIndexQuery>(query, {})
  const [_, refetch] = useRefetchableFragment(fragment, data.myObject)
  
  const onClick = () => {
    refetch({})
  }
  
  return (
    <main>
      <div>{JSON.stringify(data)}</div>
      <button onClick={onClick}>
        refetch
      </button>
    </main>
  )
}

画面はこんな感じですね。

スクリーンショット 2023-12-03 20.51.03.png

早速叩いてみます。
ページロード時はPageクエリが叩かれるためnodeは利用されません

スクリーンショット 2023-12-03 20.49.02.png

が、更新ボタン押下時に行われるrefetchの場合はnodeクエリが発行され、NodeResolverが利用されていることが確認できました。

スクリーンショット 2023-12-03 20.49.19.png

サンプルコードはこちらです。

まとめ (再掲)

  • Relay GraphQL Clientがnodeクエリを利用し、NodeResolverをコールするタイミングを明らかにした。
  • @refetchableを利用すると、relay-compilerが、nodeクエリを利用するようなクエリを生成することがある。
  • RefetchableFragmentを利用しない場合、nodeクエリと、NodeResvolerは不要になる(と思われる)。

他にもこのケースでnodeクエリが発行されることがあるよ、という情報をお持ちの方はぜひ共有いただけると幸いです。

  1. GraphQL Server Specificationでは、nodeクエリが実装されているため、筆者が必要になると判断した。Relay本家が明言しているわけではない。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1