Next.js x URQLでGraphQL Code Generatorを使う

## これはなに？

個人的な興味でGraphQLに入門したのでその導入手順を書き残しておこうと思います。

この記事が新たにGraphQLやURQLに入門する方の手助けになれば幸いです。

## この記事のゴール

以下の理解を得ることをゴールとします

- URQLを用いてGraphQLコードの自動生成が行えるようになること

## この記事に書かれていないこと

- NestJSのGraphQL Server設定方法
- NestJS側のSchema作成方法
- モノレポ環境の設定方法

## 導入

早速ですが、導入していきます。

https://github.com/mu-suke/monorepo_sample_app

上の実際のコードを参照していただければわかりますが、以下の環境で実装していきます。

| 箇所 | 選定 |
| --- | --- |
| フロントエンド | Next.js |
| サーバーサイド | NestJS |
| コード自動生成ツール | GraphQL Code Gen |
| GraphQL Client | URQL |

### 1. 必要なパッケージのインストール

まずはコード自動生成ツールのGraphQL Code GeneratorのCLI（以下、codegen)を導入していきます。

CLIのインストール

```bash
$ npm install @graphql-codegen/cli
```

↓参考

https://www.graphql-code-generator.com/docs/getting-started/installation

URQL用のコード生成パッケージのインストール

```bash
$ npm install @graphql-codegen/typescript
$ npm install @graphql-codegen/typescript-operations
$ npm install @graphql-codegen/typescript-urql
```

:::note warn
**2022.3.20追記**
公式では`@graphql-codegen/typed-document-node`もインストールするように書かれていますが、
コード生成した際に`@graphql-codegen/typescript-urql`での生成とバッティングするためインストールをしていません。

※この問題に関しては以下のissueでも上がっていました。
https://github.com/dotansimha/graphql-code-generator/issues/7578

指摘ありがとうございます！ @masashi-sutou
:::

↓参考

https://www.graphql-code-generator.com/docs/guides/react#apollo-and-urql

https://www.graphql-code-generator.com/plugins/typescript-urql

### 2. codegenの初期化と自動生成の実行

初期化の実行

```bash
$ npx graphql-codegen init

$ npm run install # 選択したプラグインをインストール
```

`graphql-codegen init`で入力した内容は以下になります。

```bash
➜  web git:(main) ✗ npx graphql-codegen init                        

    Welcome to GraphQL Code Generator!
    Answer few questions and we will setup everything for you.
  
? What type of application are you building? Application built with React
? Where is your schema?: (path or url) ../api/src/schema.gql
? Where are your operations and fragments?: src/**/*.graphql
? Pick plugins: TypeScript (required by other typescript plugins), TypeScript Operations (operations and fragments), TypeScript React Apollo (typed components and HOCs)
? Where to write the output: src/generated/graphql.tsx
? Do you want to generate an introspection file? Yes
? How to name the config file? codegen.yaml
? What script in package.json should run the codegen? generate
```

コードの自動生成です

正常に生成されると以下のような表示になります。

```bash
$ npm run generate                                  

> project-name@0.1.0 generate
> graphql-codegen

  ✔ Parse configuration
  ✔ Generate outputs
```

### 3. 動作確認用のページの作成

自動生成されたコードで動作確認をしてみます。

今回は以下のようなAPIを作成しました。

```graphql:schema.gql
# ------------------------------------------------------
# THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
# ------------------------------------------------------

"""
A date-time string at UTC, such as 2019-12-03T09:54:33Z, compliant with the date-time format.
"""

type HealthCheck {
  message: String!
}

type Query {
  healthCheck: HealthCheck!
}
```

HealthCheckを呼び出したらレスポンスで`healthy`という文字列を返すシンプルな形です。

codegenの初期化時に自動作成された`codegen.yaml`の内容は以下になります

```yaml:codegen.yaml
overwrite: true
schema: "../api/src/schema.gql"
documents: "src/graphql/**/*.graphql"
generates:
  src/generated/graphql.tsx:
    plugins:
      - "typescript"
      - "typescript-operations"
      - "typescript-urql"
  ./graphql.schema.json:
    plugins:
      - "introspection"
```

`npm run generate`で生成されたコードはこちらになります。


```tsx:src/generated/graphql.tsx
import gql from 'graphql-tag'
import * as Urql from 'urql'
export type Maybe<T> = T | null
export type InputMaybe<T> = Maybe<T>
export type Exact<T extends { [key: string]: unknown }> = {
  [K in keyof T]: T[K]
}
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & {
  [SubKey in K]?: Maybe<T[SubKey]>
}
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & {
  [SubKey in K]: Maybe<T[SubKey]>
}
export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
  String: string
}

export type HealthCheck = {
  __typename?: 'HealthCheck'
  message: Scalars['String']
}

export type Query = {
  __typename?: 'Query'
  healthCheck: HealthCheck
}

export type HealthCheckQueryVariables = Exact<{ [key: string]: never }>

export type HealthCheckQuery = {
  __typename?: 'Query'
  healthCheck: { __typename?: 'HealthCheck'; message: string }
}

export const HealthCheckDocument = gql`
  query HealthCheck {
    healthCheck {
      message
    }
  }
`
```


```tsx:src/pages/healthCheck.tsx
const HealthCheck: NextPage = () => {
  // HealthCheck APiの呼び出し
  const [result] = useHealthCheckQuery()

  if (!result.data) {
    return <Text>Error Occurred</Text>
  }

  const message = result.data.healthCheck.message

  return <Text>{message}</Text>
}

export default HealthCheck
```

表示確認
![スクリーンショット 2022-03-19 5.27.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316760/c9607ce7-e19b-0140-4304-1e090cb1e3da.png)


## 余談

今までNoSQLを使うことが多かったのでGraphQLを触ってこなかったのですが、RDB系を使う時にこれはもう外せないなというイメージでした。

引き続き学んでは記事を書いてを繰り返していこうと思います。

ではまた！

## 参考記事

https://zenn.dev/musuke/scraps/b3ee57595a1f9b

https://www.graphql-code-generator.com/

https://formidable.com/open-source/urql/docs/basics/react-preact/