[GraphQL × Nextjs] Code Generator の活用プラクティス

# 概要

本記事では **Code Generator** を活用した GraphQL × Nextjs の開発プラクティスについて紹介します。

# Codegenとは

`GraphQL Code Generator（codegen）`は、GraphQLスキーマとクエリから**TypeScriptの型定義を自動生成**するツールです。

手動で型を書く必要がなく、`GraphQL`スキーマの変更に自動で追従できるため、型安全で保守性の高いアプリケーションを構築できます🙋‍♂️

# セットアップ

### パッケージのインストール

```bash
npm install --save-dev @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations @graphql-codegen/typescript-document-nodes
```

### codegen.yml の作成

```yaml:codegen.yml
overwrite: true
schema: "http://localhost:5000/graphql"
documents: "src/**/*.graphql"
generates:
  src/generated/graphql.ts:
    plugins:
      - typescript
      - typescript-operations
      - typed-document-node
    config:
      scalars:
        ISO8601DateTime: string
```

### package.jsonにスクリプト追加

```json:package.json
{
  "scripts": {
    "codegen": "graphql-codegen --config codegen.yml",
    "codegen:watch": "graphql-codegen --config codegen.yml --watch"
  }
}
```

# ワークフロー

### ディレクトリ構成（推奨例）

```yaml
src/
├── graphql/
   ├── queries/ # 実行するクエリファイルを格納
   │   ├── getPosts.graphql
   │   └── getPost.graphql
   ├── mutations/  # 実行するミューテーションファイルを格納
   │   ├── createPost.graphql
   │   └── updatePost.graphql
   └── generated.ts # Codegenにより生成されるファイル
```


```bash
# 1. GraphQLクエリファイルを作成
# 例）src/graphql/queries/getPost.graphql...

# 2. 型を生成
npm run codegen

# 3. 生成された型を使用
import { GetPostQuery, GetPostDocument } from './generated'
```

### 開発フロー

**GraphQLクエリの作成**
```graphql:src/graphql/queries/getPost.graphql
query GetPost($id: ID!, $first: Int!) {
  published {
    post(id: $id) {
      id
      title
      content
    }
  }
}
```

**型の生成**
```bash
npm run codegen

# watch mode
npm run codegen:watch
```

**生成された型の使用**
```ts
import { GetPostDocument, GetPostQuery } from '@/generated/graphql';

export const getPost = async (id: string): Promise<GetPostQuery> => {
  const { data } = await query({
    query: GetPostDocument,
      variables: { id }
    });
 
    return data;
};
```

# Codegenによって生成される型と活用シチュエーション

### 1. **Document型** - `[QueryName]Document`
**用途**: GraphQLクエリの実行時に使用

```ts
// 生成例
export const GetPostDocument = ...;

// 使用シチュエーション
const { data } = await client.query({
  query: GetPostDocument, // ← 実行可能なクエリとして使用
  variables: { id: "123" }
});
```

### 2. **Query型** - `[QueryName]Query`
**用途**: クエリ結果の型安全な受け取り・処理

```ts
// 生成例
export type GetPostQuery = {
  published: {
    post: {
      id: string;
      title: string;
      content: string;
    } | null;
  };
};

// 使用シチュエーション
const processPostData = (data: GetPostQuery) => {
  const post = data.published.post;
  if (post) {
    console.log(post.title); // 型安全にアクセス
  }
};
```

### 3. **Variables型** - `[QueryName]QueryVariables`
**用途**: クエリ変数の型チェック

```ts
// 生成例
export type GetPostQueryVariables = {
  id: string;
};

// 使用シチュエーション
const fetchPost = async (id: string) => {
  const variables: GetPostQueryVariables = {
    id
  };
  
  return await query({ query: GetPostDocument, variables });
};
```

### 4. **Mutation型** - `[MutationName]Mutation`
**用途**: Mutation結果の型安全な処理

```tsx
// 生成例
export type CreatePostMutation = {
  createPost: {
    id: string;
    title: string;
    success: boolean;
  };
};

// 使用シチュエーション
const handleCreatePost = async () => {
  const result = await mutate({ mutation: CreatePostDocument });
  
  if (result.data) {
    const newPost: CreatePostMutation = result.data;
    console.log(`Created post: ${newPost.createPost.title}`);
  }
};
```

### 5. **MutationVariables型** - `[MutationName]MutationVariables`
**用途**: Mutation入力値の型チェック

```tsx
// 生成例
export type CreatePostMutationVariables = {
  title: string;
  content: string;
  tags?: string[];
};

// 使用シチュエーション
const createPost = async (postData: CreatePostMutationVariables) => {
  // 入力値が型チェックされる
  return await mutate({
    mutation: CreatePostDocument,
    variables: postData
  });
};
```

### 6. **Fragment型** - `[FragmentName]Fragment`
**用途**: 再利用可能なデータ構造の型定義

```tsx
// GraphQLフラグメント
fragment PostSummary on Post {
  id
  title
  publishedAt
}

// 生成例
export type PostSummaryFragment = {
  id: string;
  title: string;
  publishedAt: string;
};

// 使用シチュエーション
const PostCard = ({ post }: { post: PostSummaryFragment }) => (
  <div>
    <h3>{post.title}</h3>
    <time>{post.publishedAt}</time>
  </div>
);
```

# まとめ

便利ですね 👌

型生成を開発フローに組み込み、型安全なGraphQLアプリケーションを構築しましょう。