本記事のまとめ
- 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>
)
}
画面はこんな感じですね。
早速叩いてみます。
ページロード時はPageクエリが叩かれるためnode
は利用されません
が、更新ボタン押下時に行われるrefetchの場合はnode
クエリが発行され、NodeResolverが利用されていることが確認できました。
サンプルコードはこちらです。
まとめ (再掲)
- Relay GraphQL Clientが
node
クエリを利用し、NodeResolverをコールするタイミングを明らかにした。 -
@refetchable
を利用すると、relay-compilerが、node
クエリを利用するようなクエリを生成することがある。 - RefetchableFragmentを利用しない場合、
node
クエリと、NodeResvolerは不要になる(と思われる)。
他にもこのケースでnode
クエリが発行されることがあるよ、という情報をお持ちの方はぜひ共有いただけると幸いです。
-
GraphQL Server Specificationでは、
node
クエリが実装されているため、筆者が必要になると判断した。Relay本家が明言しているわけではない。 ↩