Closed9

GraphqlQL Code Generator で react-query の hook を自動生成したい

nbstshnbstsh

GraphqlQL Code Generator で react-query の hook を自動生成する方法をメモしていく。

nbstshnbstsh
nbstshnbstsh

Install

yarn add @graphql-codegen/typescript-react-query
yarn add @graphql-codegen/typescript
yarn add @graphql-codegen/typescript-operations
nbstshnbstsh

Configure

codegen.yaml
schema: path/to/schema
documents: './src/**/*.tsx'
generates:
  ./graphql/generated.ts:
    plugins:
      - typescript
      - typescript-operations
      - typescript-react-query
    config:
      fetcher: fetch
nbstshnbstsh

package.json にコマンド追加

package.json
{
  "scripts": {
    "generate": "graphql-codegen"
  }
}
nbstshnbstsh

fetcher について

デフォルトの fetcher の場合、毎回 endpoint url や header 情報など同じようなコードを何度も書かなければならずしんどい。

そこで、custom fetcher を作成し、これらの処理を共通化していく。

custom fetcher を利用

config の fetcher を以下のように設定すればOK。

codegen.yaml
    config:
      fetcher:
+        func: 'graphql/custom-fetcher#useFetchData'
  • func: 'graphql/custom-fetcher#useFetchData'
    => graphql/custom-fetcher の file 内から named export されている useFetchData を custom fetcher として指定

custom fetcher として hook を利用

codegen.yaml
    config:
      fetcher:
        func: 'graphql/custom-fetcher#useFetchData'
+        isReactHook: true
  • isReactHook: true
    => func で指定した関数が hook である場合は true。この値によって、生成される hook のコードが内部に custom hook を含める形で生成される。false のまま、custom fetcher を hook で作成してしまうと渡される引数が異なったり、Mutation 時にエラーが起きたりするので注意。
graphql/custom-fetcher.ts
export const useFetchData = <TData, TVariables>(
  query: string,
  options?: RequestInit['headers']
): ((variables?: TVariables) => Promise<TData>) => {
  // it is safe to call React Hooks here.
  const { url, headers } = React.useContext(FetchParamsContext)

  return async (variables?: TVariables) => {
    const res = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        ...headers,
        ...(options ?? {})
      },
      body: JSON.stringify({
        query,
        variables
      })
    })

    const json = await res.json()

    if (json.errors) {
      const { message } = json.errors[0] || 'Error..'
      throw new Error(message)
    }

    return json.data
  }
}

内部で custom hook を利用して context からの値の受け渡しが可能になるので、デフォで "ログイン情報をもとに token を Authorization header に付与する" といった boilerplate になりがちなコードを組み込むことも可能。

nbstshnbstsh

Query Key

cache された特定の query data を更新する際に、Query Key が必要になる。

e.g.) Mutation 後に local のデータを更新する

queryClient.setQueryData(myQueryKey, data)

https://react-query.tanstack.com/guides/updates-from-mutation-responses

Graphql Code Generator で hooks を自動生成すると、key も自動で生成される。
cache data を更新する際に必要になるので、この自動生成された key を取得できるようにしておくと良い。

exposeQueryKeys: true を追加

config に exposeQueryKeys: true に追加すればOK。

codegen.yml
generates:
  ./src/graphql/generated.ts:
    plugins:
      - typescript
      - typescript-operations
      - typescript-react-query
    config:
      fetcher:
        func: '@/graphql/custom-fetcher#useFetchData'
        isReactHook: true
+      exposeQueryKeys: true

すると、以下のような key を取得するための関数が hook に生える。

graphql/generated.ts
useUserQuery.getKey = (variables: UserQueryVariables) => ['User', variables];

こんな感じで利用できる↓

const queryKey = useUserQuery.getKey({ id: 'user_id' });
queryClient.setQueryData(queryKey, updatedUser)
このスクラップは2022/06/19にクローズされました