React Query と GraphQL Code Generator 初期設定

React Query で GraphQL のリクエストを行おうとしたが初期設定が結構面倒だったのでメモ。

開発環境

  • react: 18.0.0
  • graphql: 15.8.0
  • @graphql-codegen/typescript: 2.4.11
  • @graphql-codegen/typescript-operations: 2.4.0
  • @graphql-codegen/typescript-react-query: 3.5.12
  • react-query: 3.39.1
  • typescript: 4.6.3
  • vite: 2.9.7

経緯

まず React Query の Installation を確認してインストールするところまではいい。

次に Quick Start を見るのだが、

import {
    useQuery,
    useMutation,
    useQueryClient,
    QueryClient,
    QueryClientProvider,
} from 'react-query'
import { getTodos, postTodo } from '../my-api'

import { getTodos, postTodo } from '../my-api' というのが出てきて、my-api なるファイルの中身が示されていないので、これを見ても Quick Start できない。

次に GraphQL を見てみるも、今度は GraphQL Code Generator を使用するサンプルであり、React Query 単独で使う例ではない。

ということで React Query の公式サイトが不親切なために初期設定に手間取ったので、React Query と GraphQL Code Generator を用いる場合の初期設定の結論をメモする。

初期設定

1. React Query のインストール

前述の通り React Query の Installation を確認してインストールする。

npm i react-query

2. GraphQL Code Generator のインストール

Installation を見てインストールする。

npm install graphql
npm install @graphql-codegen/cli

Setup は飛ばして Guide: React and GraphQL を見る。以下のプラグインをインストールする。

npm install @graphql-codegen/typescript-react-query
npm install @graphql-codegen/typescript
npm install @graphql-codegen/typescript-operations

3. 型と hooks 自動生成

Guide: React and GraphQL の 2. Configure the plugin の内容に当たるが、ここで一番ハマった。

まず ./src/api に下記の 2 ファイルを作る。

次に ./codegen.yaml を下記のように作る。

overwrite: true
generates:
  ./src/api/generated.ts:
    schema: ./src/api/schema.graphqls
    documents: ./src/api/query.graphql
    plugins:
      - typescript
      - typescript-operations
      - typescript-react-query
    config:
      errorType: "any"
      fetcher:
        endpoint: "import.meta.env.VITE_API_BASE_URL"
        fetchParams: { headers: { "Content-Type": "application/json" } }

公式サイトと異なる点としては、まず schema はローカルにあるファイルを指定している。

また documents は graphql ファイルとしている。

更に config.errorType について、何も指定しないと型が unknown になる。
プラグインの説明を見てデフォルトの型らしい TError を指定しても型が見つからない的なエラーとなってしまったので、仕方なく any を指定している。

endpoint は環境変数で URL を定義し、それを指定している。
環境変数の作り方については環境構築方法によるので割愛する。私のは Vite の環境変数。

fetchParams には Content-Type を指定している。
私はバックエンドを Go の gqlgen で作っているのだが、Content-Type を application/json に指定しないとエラーとなってしまうため。

次に 3. Run the codegen and update your code に記載の通り package.json に

{
  "scripts": {
    "generate": "graphql-codegen"
  }
}

この内容を追記する。
大抵既に scripts の定義があるんじゃないかと思うが、その場合は下記のように既存の定義に追記する。(これは Vite の例)

{
    "name": "front",
    "private": true,
    "version": "0.0.0",
    "scripts": {
        "dev": "vite",
        "build": "tsc && vite build",
        "preview": "vite preview",
        "generate": "graphql-codegen"
    },
    // 以下省略
}

これでプロジェクトルート(codegen.yaml と同じ階層)で

npm run generate

のコマンドを実行すると、./src/api/generated.ts が生成される。

4. React で使う

./src/main.tsx (もしくは index.tsx とか別の名前かもしれない)を下記のようにする。

import React from 'react'
import ReactDOM from 'react-dom/client'
import {
  QueryClient,
  QueryClientProvider,
} from 'react-query'
import App from './App'
import './index.css'

const queryClient = new QueryClient()

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
        <App />
    </QueryClientProvider>
  </React.StrictMode>
)

次に使いたいところで下記のようにする。
※今回は ./src/api/query.graphql にfindBlogs という queryを定義しているとする。

import { useFindBlogsQuery } from '../../api/generated'

export function Home() {
  const { status, data, error, isFetching } = useFindBlogsQuery({});
  return (
    <div>
        {status === "loading" ? (
            <span>Loading...</span>
        ) : status === "error" ? (
            <span>Error: {error?.message}</span>
        ) : (
            <>
            {isFetching ? (
                <span>Loading...</span>
            ) : (
                <div>
                {data?.blogs?.map((blog) => (
                    <p key={blog?.id}>
                    {blog?.name}
                    </p>
                ))}
                </div>)}
            </>
        )}
    </div>
  );
}

これで query を投げることができる。

以上