graphql codegenでhooksを生成する方法、および少し詰まった点

# 行き詰まった経緯

`graphql`の練習としてフロントエンドで`codegen`を使用して自動的にコード生成をしようとしていた。

以前も使用したことはあったので多少は設定に困らなかったが、今回はhooksも自動生成しようと思って、設定内容を変えてみた。
実装の概略は次章で説明する。
問題となったエラーは以下の通り
```the name `GetUsersDocument` is defined multiple times```

# 実装

codegenをinstall

`pnpm i -D graphql @graphql-codegen/cli`

`codegen`の初期化
    `pnpm grapql-codegen init`

対話形式で初期化が始まる(今回はNextjsのプロジェクトであるためReactを選択)
```
? What type of application are you building? (Use arrow keys)
  Backend - API or server 
  Application built with Angular 
❯ Application built with React 
  Application built with Stencil 
  Application built with Vue 
  Application using graphql-request 
  Application built with other framework or vanilla JS 
```
`schema`の場所の指定(今回は`localhost:8080`にgraphqlサーバーが立っている)
 ```
? Where is your schema?: (path or url) http://localhost:8080/v1/graphql
```

`graphql`ドキュメントを読み込むパスの指定
```
? Where are your operations and fragments?:src/graphql/**/*.graphql
```

生成されたコードをどこに出力するかの設定(今回は`src/generates/`)
```
? Where to write the output: src/generates/
```
`introspection file`を作るかどうか （今回はyes）
```
? Do you want to generate an introspection file? (y/N) 
```
コンフィグファイルの名前(今回はデフォルト)
```
? How to name the config file? (codegen.ts) 
```
npmコマンドの名前(これもデフォルト)
```
? What script in package.json should run the codegen? (codegen) 
```


対話終了
```
Fetching latest versions of selected plugins...

    Config file generated at codegen.ts

      $ npm install

    To install the plugins.

      $ npm run codegen

    To run GraphQL Code Generator.
```
`npm install`をしてねって言われてるので
`pnpm install`を実行


試しにクエリドキュメント作成
```graphql:src/graphql/query/getUser.graphql
query GetUsers {
    users {
        id
        userName
    }
}
```
データベースには`id`と`userName`のcolumnを持つ`users`テーブル

`codegen`を実行するときは
`npm run codegen`を使ってねって言われてるので
`pnpm codegen`を実行

構造は以下の通り
```
src
├── generates
│   ├── fragment-masking.ts
│   ├── gql.ts
│   ├── graphql.ts
│   └── index.ts
├── graphql
│   └── query
│       └── getUser.graphql
```

ここまでで単純なgraphqlのコード生成は終了

# 本題
今回の目標は`hooks`を生成すること

必要なライブラリをインストール
`npm i -D @graphql-codegen/typescript @graphql-codegen/typescript-react-apollo     @graphql-codegen/typescript-operations @apollo/client`

`codegen.ts`を次のように編集

```ts:./codegen.ts
import type { CodegenConfig } from "@graphql-codegen/cli";

const config: CodegenConfig = {
    overwrite: true,
    schema: "http://localhost:8080/v1/graphql",
    documents: "src/graphql/**/*.graphql",
    generates: {
        "src/generates/": {
            preset: "client",
            plugins: [
                "typescript",
                "typescript-operations",
                "typescript-react-apollo",
            ],
            config: {
                skipTypename: false,
                withHOC: true,
                withComponent: false,
                scalars: {
                    uniqueidentifier: "string",
                },
            },
        },
        "./graphql.schema.json": {
            plugins: ["introspection"],
        },
    },
};

export default config;

```

`plugins`で使用するpluginを設定

`config`で生成されるコードの詳細を設定できる

諸々を省くが実際にクエリを叩いたところ以下エラーが出力された

```
./src/generates/graphql.ts
NonErrorEmittedError: (Emitted value instead of an instance of Error) 
  x the name `GetUsersDocument` is defined multiple times
```

エラーの内容としてはgraphql.tsファイル内で`GetUserDocument`が複数宣言されていることだった

`codegen.ts`を以下のように編集するとエラーは改善された

```ts:./codegen.ts
generates: {
        "src/generates/graphql.ts": { //出力先の変更
                            //presetを削除
            plugins: [
                "typescript",
                "typescript-operations",
                "typescript-react-apollo",
            ],
            config: {
                skipTypename: false,
                withHOC: false,
                withComponent: false,//出力するファイルを一つに設定する
                scalars: {
                    uniqueidentifier: "string",
                },
            },
        },
        "./graphql.schema.json": {
            plugins: ["introspection"],
        },
    },
```

どうやら`preset:"client"`が悪さをしていたみたい


そもそも`preset`とはなんなのか

[公式ドキュメント](https://the-guild.dev/graphql/codegen/docs/config-reference/codegen-config#:~:text=Custom%20Plugins\)-,generates.preset,-%2D%20A%20list%20of)によると
```generates.preset - A list of presets to use for the output. Presets are a way to dynamically create the list of output files based on the input schema. near-operation-file-preset is a good example```
とされている


`codegen`で生成されるコードと`preset`で生成されたコードが重複してエラーが出力されていた感じなのだろうか(ちゃんと理解できてない分野であるため下手なことが言えない)


# hooksを出力するメリット
なぜ今回出力先をhooksにしたのかについて
`withHooks:true`にせずにデフォルトで使用する場合データを取得するクエリは以下のように書く必要がある(本記事では`codegen`を主に取り扱っているため`apollo client`に関して説明はしない)
```ts:hoge.tsx
const getUsersDoc = gql`
    query GetUsers {
        users {
            id
            userName
        }
    }
`
const {data} = useQuery(getUserDoc)
```

しかし設定を適用すると

```graphql:/graphql/query/getUser.graphql
query GetUsers {
    users {
        id
        userName
    }
}
```
```ts:hoge.tsx
    const { data } = useGetUsersQuery();
```

といった感じでクエリと関数を別ファイルに分けることができる上に関数の命名がドキュメントに基づいた名前になるため直感的にわかりやすいと感じた。
`hooks`出力にしなくてもドキュメントは別ファイルに分けることはできると言われればそれはそうとしか返せない。
関数名が変わるのが私個人としてはわかりやすくていいかなあといった感じだった。
そこもドキュメントの名前見ればわかるだろうと言われればそれもそうなのかもしれない

その他ここは違うなどの意見がございましたら教えていただけると幸いです。

## 参考
https://the-guild.dev/graphql/codegen/docs/config-reference/codegen-config

https://www.apollographql.com/docs/react/data/queries