開発

GraphQL Code Generatorとは?client presetの設定とv7の変更点を実装目線で解説

GraphQL Code Generatorは、GraphQLスキーマと手元のオペレーションを読み取って、TypeScriptの型やクライアント用のコードを生成するCLIです。npm registryで2026年9月6日に確認した@graphql-codegen/cliのlatestは7.4.0(2026年9月1日公開)で、直近の週間ダウンロードは770万件を超えていました。スキーマを手で写した型定義を保守する必要がなくなる一方、生成物の形はcodegen.tsの書き方で大きく変わるツールでもあります。この記事では、生成の流れ、設定ファイルの最小構成、スキーマ取得からCIまでを通しで動かす手順、client presetが出すもの、サーバー側の型生成、そしてv7とclient preset v6で入った破壊的変更までを実装目線で扱います。

まとめ:GraphQL Code Generatorを入れてよい条件

先に結論を置きます。TypeScriptでGraphQLのクライアントを書いていて、スキーマが自分たちの管理下にあるか少なくとも安定して取得できるなら、client presetを使った導入はほぼそのまま通ります。codegen.tsに3行書いてnpx graphql-codegenを叩けば、クエリの引数と戻り値に型が付いた状態から始められるためです。手書きの型定義とスキーマがずれて実行時に落ちる、という事故を構造的に潰せる点が最大の見返りになります。

逆に導入を急がないほうがよい場面が3つあります。第一に、Node 20以下から上げられない環境。v7はNode 20のサポートを落としており、Node 22以上が前提になりました。第二に、スキーマが日次で壊れるほど流動的な段階のプロジェクト。生成物が毎回入れ替わり、レビューの差分がノイズで埋まります。第三に、GraphQL自体をまだ採用していない、あるいはクライアントとサーバーを同一リポジトリで書いているケース。後者はtRPCとは?REST・GraphQL・gRPCとの違いと型安全なAPI実装で扱ったような、コード生成を介さずに型を共有する手段のほうが手数が少なく済みます。

導入の可否を判断する前に、そもそもGraphQLをAPI方式として採るかを決めていない段階なら、REST APIとGraphQLの違いと使い分け|選定基準と運用コストで運用コストの差を確認してからでも遅くありません。コード生成はGraphQLを選んだあとの実装効率の話であって、方式選定そのものを覆す材料にはならないからです。

GraphQL Code Generatorがスキーマから型を起こす仕組みと守備範囲

まず、このツールが何を入力に何を出しているのかを押さえます。ここを曖昧にしたまま設定を写経すると、生成物が期待と違ったときに切り分けられません。

スキーマとオペレーションを入力にTypeScriptの型を出力する流れ

入力は2種類あります。ひとつはスキーマで、SDLのファイルパスでも稼働中のエンドポイントのURLでも指定できます。もうひとつがオペレーション、つまりアプリ側に書いたクエリ・ミューテーション・サブスクリプションの定義。この2つを突き合わせ、指定したプラグインまたはpresetの形式で出力するのが処理の全体像で、公式のGetting Startedも同じ順序で説明しています。

スキーマ側の設計思想についてはGraphQLスキーマ設計の核心原則に整理してあり、GraphQLそのものの前提を確認したい場合はGraphQLとは?REST APIとの違い・メリット・デメリットを先に読むと入力側の理解が揃います。生成器は与えられたスキーマを疑わないため、スキーマが雑なら出てくる型も雑になる、という関係は変わりません。

7.4系という版番号とnpmで配られている主要パッケージの構成

パッケージは本体と機能別に分かれています。npmのパッケージページで2026年9月6日に実測した各latestは次のとおりでした。表の先頭列では、見出しに示した共通の名前空間を省略し、各パッケージを見分ける末尾の名称だけを記載しています。版を照合する際は、見出しと先頭列をつないだ完全な名称で確認してください。

@graphql-codegen/以下 公開日 役割
cli 7.4.0 2026-09-01 実行本体とCLI
client-preset 6.1.3 2026-08-12 クライアント一式の生成
typescript 6.1.0 2026-07-06 スキーマ由来の型
typescript-operations 6.1.6 2026-08-12 操作ごとの型
typescript-resolvers 6.1.0 2026-07-06 サーバー側の型

CLIとpresetで版系列が別々に進む構造なので、CHANGELOGを追うときは両方を見る必要があります。実際、7.2.0が出た2026年7月以降もCLIは7.4.0まで3段階進み、client-presetは6.1.0から6.1.3まで修正版が続きました。週間ダウンロードは@graphql-codegen/cliが7,722,977件、graphql本体が48,411,820件(いずれも2026年8月23日から29日の集計)で、GraphQLを入れているプロジェクトの一定割合がこのCLIを併用している規模感になります。

GraphQL本体の版とコード生成器の版を対応させる確認の手順

意外と踏み抜かれるのがgraphql本体との版の噛み合わせです。CLI 7.2.0のCHANGELOGには、peerDependenciesのgraphql指定に^17.0.0が追加されたと記録されています。同日時点でgraphql本体のlatestは17.0.2(2026年7月3日公開)でした。16系のまま止まっているリポジトリでも動きますが、17系へ上げるときにpeer警告が出るなら、まずCLI側を7.2.0以上へ寄せてから本体を上げる順序が安全です。

言語仕様そのものはGraphQL仕様の2021年10月版が公開版として参照されており、@deferのような後発のディレクティブは仕様の版とライブラリの実装が一致しない期間があります。生成される型が想定と違うとき、原因がツール側なのか仕様側なのかを切り分ける起点として、仕様の版も控えておくと調査が短く済みます。GraphQLの周辺構成をどこまで広げるかは、GraphQLフェデレーションとは?サブグラフ合成とルーターの実装・採用判断GraphQLサブスクリプションとは?graphql-wsでの実装とスケール設計で扱った構成要素と合わせて決める話です。

codegen.tsを探しに行く設定探索とjitiでの読み込み

設定ファイルの発見にはcosmiconfigが使われ、codegen.tscodegen.jscodegen.ymlcodegen.json.graphqlrcpackage.jsoncodegenキーが候補になります。TypeScriptで書いた設定はjitiローダー経由で読み込まれ、.mts.ctsも通ります。探索順と対応拡張子は公式のcodegen.ts fileに列挙されているので、想定した設定が読まれないときはここを照合してください。

実務ではcodegen.tsを選ぶのが素直です。CodegenConfig型を当てられるため、設定のtypoがエディタ上で分かります。複数の設定を使い分けたいときは--config-c)でファイルを名指ししてください。

codegen.tsの最小構成とCLIから生成を回すまでの手順

次に、実際に動かすところまでを追います。手数は少なく、初回はウィザードに任せる手もあります。

initウィザードで入る依存パッケージと生成される初期ファイル

ゼロから始めるなら、対話式のセットアップが用意されています。

npx graphql-code-generator init

スキーマの場所、対象フレームワーク、プラグインの選択、出力先までを順に聞かれ、選んだ内容に応じた設定ファイルとnpmスクリプトが書き出されます。ウィザードが提示するパッケージを自分でインストールすれば準備完了。公式のInstallationに載っている手で入れる場合の最小構成は、ランタイム依存のgraphqlと、開発依存の@graphql-codegen/clitypescriptの3つです。watchモードを使う予定があるなら@parcel/watcherも足しておきます。

client presetを使うcodegen.tsの最小構成と各項目の意味

設定はプロジェクト直下に置きます。client presetを使う場合、書く項目は実質3つだけです。

import { CodegenConfig } from '@graphql-codegen/cli'

const config: CodegenConfig = {
  schema: 'http://localhost:4000/graphql',
  documents: ['src/**/*.tsx'],
  generates: {
    './src/gql/': {
      preset: 'client'
    }
  }
}

export default config

schemaはURLでもローカルのSDLファイルでも構いません。documentsはオペレーションを探すglobで、.tsxの中に埋め込んだ文字列も拾えます。generatesのキーが出力先で、client presetを使うときはファイル名ではなくディレクトリを末尾スラッシュ付きで指定する点に注意してください。ここをファイル名にすると生成が失敗します。

watchモードとCIで差分を検出するcheckオプションの使い分け

開発中は--watch-w)を付けて常駐させ、ソースを保存するたびに再生成させるのが定番の運用です。globを渡して監視対象を絞ることもできます。

graphql-codegen --config codegen.ts --watch

一方CIでは--checkを使います。これはドライラン相当の検証モードで、生成物をコミットする運用にしているとき、「生成し直すと差分が出る=生成物が古い」状態を落とせます。生成物をリポジトリに含めるかどうかは方針が分かれるところですが、含めるなら--checkのジョブをCIに必ず1本置いてください。含めないなら、ビルド前にgraphql-codegenを走らせるnpmスクリプトへ組み込む形になります。デバッグしたいときは--debug-d)が使え、常駐と単発の使い分けは公式のDevelopment Workflowにも整理されています。

ローカルでスキーマを取得して型生成とCIチェックまでを通しで動かす

ここからは、設定の意味ではなく手順として並べます。既存のリポジトリへ後から入れる場合、詰まるのはたいてい「スキーマをどこから取るか」と「誰がいつ生成を回すか」の2点だからです。

エンドポイント指定とSDLファイル指定でschemaの書き方を分ける

schemaには配列を渡せます。ローカルのSDLと稼働中のエンドポイントを混在させることもでき、認証が要るエンドポイントにはヘッダを添えられます。公式のschema fieldに指定形式が列挙されているので、複数ソースを束ねるときはそちらを見てください。

import type { CodegenConfig } from '@graphql-codegen/cli'

const config: CodegenConfig = {
  schema: [
    './schema/*.graphql',
    {
      'https://api.example.com/graphql': {
        headers: { Authorization: 'Bearer ${API_TOKEN}' }
      }
    }
  ],
  documents: ['src/**/*.{ts,tsx}', '!src/gql/**/*'],
  ignoreNoDocuments: true,
  generates: {
    './src/gql/': { preset: 'client' }
  }
}

export default config

documentsの2要素目にある!始まりのglobは除外指定です。生成先ディレクトリをdocumentsから外しておかないと、生成物の中の文字列を再びオペレーションとして拾い、実行のたびに差分が揺れます。ignoreNoDocuments: trueは、オペレーションが1件も見つからないときに落とさず続行させる指定で、watchモードで書きかけのファイルを保存したときにCLIが止まるのを防ぎます。

package.jsonのscriptsに生成とチェックのコマンドを並べる

コマンドを直接叩く運用は続きません。単発生成・常駐・CI検証の3つをスクリプトとして固定し、READMEには名前だけ書くようにします。

{
  "scripts": {
    "codegen": "graphql-codegen --config codegen.ts",
    "codegen:watch": "graphql-codegen --config codegen.ts --watch",
    "codegen:check": "graphql-codegen --config codegen.ts --check",
    "build": "npm run codegen && vite build"
  }
}

生成物をコミットしない方針なら、上のbuildのように生成をビルドの前段へ繋いでおきます。コミットする方針ならbuildから外し、代わりにcodegen:checkをCIへ載せる形。どちらかに寄せるのが要点で、両方を中途半端に混ぜると「手元では通るのにCIで落ちる」原因が生成物の鮮度なのか設定なのか判別できなくなります。

GitHub Actionsで生成物の陳腐化を検出するワークフローを置く

CI側は次の程度で足ります。Node 22を明示する点だけ落とさないでください。

name: codegen
on: [push, pull_request]
jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci
      - run: npm run codegen:check

setup-nodenode-versionを22にしているのは、CLI 7系がNode 22以上を前提にしているためです。Node.js公式のリリース一覧で各系列のサポート期限を確認したうえで、CIとローカルの版を揃えてください。ここがずれていると、手元では通るのにCIだけESM関連のエラーで落ちる、という切り分けの手間が発生します。

client presetが生成するgraphql関数と型の受け渡し

client presetは、公式が「あらゆるGraphQLクライアント向けに型安全なオペレーションを生成する既定の方法」と位置づけている出力形式です。個別のプラグインを並べる書き方より先に、こちらを検討する価値があります。

graphql関数が返すTypedDocumentNodeと型の伝わり方

生成先ディレクトリにはgraphqlという関数が出力されます。クエリ文字列をこの関数に渡すと、戻り値がTypedDocumentNodeになり、結果と変数の型が紐づいた状態でクライアントへ渡せます。

import { graphql } from './gql'

const UserQuery = graphql(`
  query User($id: ID!) {
    user(id: $id) { id name }
  }
`)

このUserQueryをApollo ClientのuseQueryやurqlに渡せば、data.user.nameが補完され、変数にidを渡し忘れればコンパイルで落ちます。対応クライアントはReact側がApollo Client・urql・graphql-request・React Query・SWR、Vue側が@vue/apollo-composable・villus・@urql/vueで、一覧は公式のClient Presetにあります。受け手の設定はApollo Clientとは?GraphQLクライアントの使い方とv4の変更点に整理済みです。

Fragment Maskingが強制するデータ依存の宣言とuseFragment

client presetの既定で有効になっているのがFragment Maskingです。フラグメントの結果をFragmentType<T>という包みに閉じ込め、useFragment()を通さないと中身へ触れないようにします。狙いは、あるコンポーネントが宣言していないフィールドを親から偶然もらって使ってしまう状態を防ぐこと。実装としては次の形になります。

import { FragmentType, useFragment, graphql } from './gql'

export const UserCardFragment = graphql(`
  fragment UserCard_user on User {
    id
    name
  }
`)

type Props = { user: FragmentType<typeof UserCardFragment> }

export function UserCard(props: Props) {
  const user = useFragment(UserCardFragment, props.user)
  return <span>{user.name}</span>
}

親コンポーネントはUserCardFragmentをクエリへ差し込むだけでよく、nameを取っているかどうかを気にせずに済みます。言い換えると、コンポーネントが必要とするデータを自分のフラグメントで宣言する設計を強制する仕掛けです。フラグメントそのものの書き方はGraphQLのFragmentとは?基礎概念、役割、定義方法とメリットにまとめてあります。既存コードへ後から入れると修正箇所が広がるため、fragmentMasking: falseで一旦切って段階移行する判断もあり得ます。

Persisted Documentsでクエリを識別子へ置き換える運用

もうひとつの機能がPersisted Documentsです。クエリ本文をハッシュ化し、通信時には識別子だけを送る形にします。転送量とバンドルサイズが減るうえ、公式が「クライアントが任意のGraphQLオペレーションを送るのを防ぐ」と書いているとおり、セキュリティ面の締めにもなります。

サーバー側が識別子とクエリの対応表を持つ前提になるので、フロントだけで完結する話ではありません。documentModestringにしてAST生成をやめる選択肢もあり、こちらはバンドルサイズを削る方向の設定です。公開APIではなく自社アプリ専用のGraphQLサーバーなら、Persisted Documentsまで踏み込む構成を検討してよいでしょう。

サーバー側の型生成とpresetとpluginsの構成を選び分ける基準

コード生成はクライアント専用ではありません。スキーマファーストで進める場合、サーバー側の実装にも同じ仕組みが効きます。

typescriptとtypescript-operationsで分かれる出力の役割

typescriptプラグインはスキーマ由来の型、つまり各オブジェクト型やinput型、enumを出します。typescript-operationsは、書いたクエリごとの結果型と変数型を出す担当。この2つは組み合わせて使うのが基本で、片方だけでは実用になりません。

client presetは内部でこの2つ相当の処理を含んでいるため、クライアント側なら個別指定は不要です。逆に、生成物のファイル分割や命名を細かく制御したい場合はplugins方式へ降りることになります。

typescript-resolversでサーバー側の実装に型を付ける

サーバーを書くならtypescript-resolversが入ります。スキーマの各フィールドに対応するリゾルバ関数のシグネチャを型として出すため、引数の取り違えや戻り値の欠けを実装時点で潰せます。設定は次のように、出力先をファイル名で指定してplugins配列を並べる形です。

import type { CodegenConfig } from '@graphql-codegen/cli'

const config: CodegenConfig = {
  schema: './schema/*.graphql',
  generates: {
    './src/generated/resolvers.ts': {
      plugins: ['typescript', 'typescript-resolvers'],
      config: {
        contextType: '../context#GraphQLContext',
        useIndexSignature: true
      }
    }
  }
}

export default config

contextTypeは自前のコンテキスト型を指す指定で、パス#エクスポート名の形で書きます。ここを省くとany相当のコンテキストが入り、リゾルバの第3引数だけ型が効かない状態になります。指定できる設定項目は公式のtypescript-resolversに一覧があるので、マッパーやフェデレーション関連の指定はそちらで確認してください。

スキーマファーストの設計では、SDLを先に固めてから型を生成し、その型に合わせてリゾルバを埋める順序になります。コードファーストのライブラリを使う場合と真逆の流れなので、チームでどちらを採るかは着手前に決めておいてください。REST側で同じ構図を経験しているなら、OpenAPI GeneratorでJavaコードを自動生成する方法で扱ったスキーマ駆動の手順と発想は共通です。

presetとpluginsをどちらで書くか決めるときの判断材料

判断は単純で、次の表の左に当てはまるならpresetを選びます。

状況 選ぶ書き方
クライアントの新規構築 client preset
出力の粒度を制御したい plugins指定
サーバーのリゾルバ生成 plugins指定
既存のplugins構成が稼働中 据え置き

presetは複数プラグインの組み合わせに名前を付けたもの、と捉えると迷いません。既にplugins方式で動いている構成を、presetへ書き換えるだけの理由は薄いでしょう。新規に書くならpresetから入り、足りなくなった時点で降りる順序が手戻りを減らします。GraphQLとgRPCを併用する構成でスキーマの生成物が二系統になる場合の整理は、gRPCとGraphQLの違いは?役割分担・スキーマ運用・BFF併用で使い分けるにまとめてあります。

v7とclient preset v6の破壊的変更と移行で詰まる箇所

ここが既存プロジェクトを上げるときの本題です。v7とclient preset v6は同じPRで入った大きな区切りで、黙って上げると型エラーが大量に出ます。

Node 22必須とESM既定になったv7の移行前チェック項目

v7.0.0の破壊的変更は3点。Node 20のサポートが落ち、Node 22以上が前提になりました。理由は依存パッケージがESM専用へ移り、Node 22のrequire()によるESM読み込みで支障がなくなったためとCLIのCHANGELOGに記されています。

CLI本体もESM版が既定になり、コマンドの系統が整理されました。CJSが必要な場合はgraphql-codegen-cjsが残されていますが、公式は新規利用を勧めておらず将来削除の予定です。3点目がnoSilentErrors: trueの既定化で、documentsのglobに構文エラーを含むファイルがあると黙って除外せず失敗します。移行時にいきなり落ちるならこれを疑ってください。JestでESM絡みの問題が出る場合はVitestを試すよう公式が案内しています。

7.3系と7.4系で足されたoverwriteとwatchの制御項目

v7.0.0の直後にも実務向けの調整が続いています。CHANGELOGを2026年9月6日時点で追うと、7.1.0でdisableFederationDirectiveAndScalarInjectionが加わりFederation v2構成での注入を止められるようになり、7.2.0でgraphql@^17.0.0がpeerDependenciesへ入りました。実運用に効くのは7.3.0以降の2件です。

足された制御 効く場面
7.1.0 Federation注入の抑止 サブグラフ構成
7.2.0 graphql 17系のpeer対応 本体の版上げ
7.3.0 overwriteのオブジェクト化 watch中の消え
7.4.0 contentComparisonの指定 手で直した出力

7.3.0ではoverwriteが真偽値だけでなくremoveStaleFilesupdateExistingFilesを持つオブジェクトを取れるようになりました。従来のoverwrite: truefalseもそのまま動くため破壊的変更ではありません。7.4.0では出力ごとにcontentComparison'cache-first''disk'で指定できるようになっています。watchモードのCLIは前回書き出した内容のハッシュを覚えていて、同じ内容なら書き込みを飛ばす作りです。生成物を人手で触ってしまうリポジトリでは、この省略のせいで手元の変更が戻らないことがあり、'disk'を指定すると実ファイルと比較して書き直します。

generates: {
  './src/gql/': {
    preset: 'client',
    contentComparison: 'disk',
    overwrite: { removeStaleFiles: false }
  }
}

あわせて7.4.0では、presetを使うgeneratesoverwriteが黙って無視されていた不具合も直っています。watch中に生成ファイルが古いものとして消される症状に心当たりがあるなら、7.4.0以上へ上げるだけで解消する可能性があります。

nullableと__typenameの生成が変わったv6の型の締まり方

client preset v6とtypescript-operations v6は、生成される型そのものを変えました。従来はnullableなフィールドが?付きの省略可能プロパティとして出ていましたが、実行時にはnullが来るだけでundefinedにはならないため、この扱いを改めています。@defer@skip@includeが付く場合だけが例外です。

あわせて、結果型に省略可能な__typenameを出す挙動も廃止されました。Apollo Clientのようにクライアントが自動注入する構成では、nonOptionalTypename: trueskipTypeNameForRoot: trueで実挙動に合わせられます。カスタムスカラーの既定型もanyからunknownへ変わり、Exactユーティリティ型はtypescriptプラグインではなくtypescript-operations側が出す形になりました。いずれも型が締まる方向の変更なので、上げた直後にエラーが増えるのは想定内。CHANGELOGの該当項目と突き合わせながら潰していく作業になります。

移行時に版を選ぶなら、client-presetは6.1.3以上を指定してください。client presetのCHANGELOGを見ると、6.1.1で@skip@includeとFragment Maskingを併用したときの生成不具合が直り、6.1.2でマスク済みフィールドが軒並み省略可能になる回帰が差し戻され、6.1.3でunionと条件付きディレクティブの組み合わせでフィールドの型が出ない不具合が修正されています。条件付きディレクティブを使うコードベースで6.1.0のまま止まっていると、この3件をまとめて踏みます。

受託案件でGraphQL Code Generatorを入れる条件と見送る場面

受託の現場で判断するなら、次の3条件が揃えば導入して問題ありません。第一に、フロントがTypeScriptで書かれていること。第二に、Node 22以上へ上げられること。第三に、スキーマの変更が週次程度のペースに収まっていること。この3つが揃うなら、初期コストは半日程度で回収できる範囲に収まります。

見送るのは、Node 20から動かせない保守案件と、スキーマがまだ日単位で入れ替わる立ち上げ初期の2つです。前者は6.x系で止める選択肢が現実的で、後者はスキーマが固まってから入れれば足ります。API開発・システム連携では、GraphQLサーバーの設計からスキーマの切り方、こうしたコード生成を含むフロント側の型運用までを一貫して受けています。既存のGraphQL APIがあり、フロントの型定義が手書きで膨らんで困っている場合は、まずdocumentsのglobがどこまで広がるかを数えるところから始めてください。

よくある質問

client presetとpluginsのどちらを使えばよいですか?

クライアント側を新規に組むならclient presetを選んでください。公式も既定の方法として案内しています。出力ファイルの分割や命名を細かく決めたい場合、あるいはサーバー側のリゾルバを生成する場合はplugins方式になります。

生成したファイルはGitにコミットすべきですか?

どちらでも運用できますが、決めたら片方に寄せてください。コミットする方針なら、CIにgraphql-codegen --checkを1本置いて生成物の陳腐化を検出します。コミットしない方針なら、ビルド前に必ず生成が走るようnpmスクリプトへ組み込みます。生成物を人手で直す運用が混ざっているなら、7.4.0のcontentComparison: 'disk'を併せて指定する方法が差分の取りこぼし防止に有効です。

Fragment Maskingは無効にできますか?

可能です。client presetのfragmentMaskingfalseにすれば従来どおりフラグメントの結果へ直接アクセスできます。ただしコンポーネントが宣言していないデータに触れる状態も戻るため、既存コードの移行が済んだら再度有効にする前提で切るのが無難でしょう。

v6から上げると型エラーが大量に出るのはなぜですか?

client preset v6とtypescript-operations v6で、nullableフィールドの省略可能扱いと__typenameの自動付与が廃止されたためです。生成される型が実行時の挙動に近づいた結果で、出ているエラーの多くは元々あった型の緩さが表面化したものと考えてください。@skip@includeを使っているなら、client-presetを6.1.3以上にしてから型エラーを数え直すと、修正すべき件数が減ります。

Node 20のままv7を使うことはできますか?

サポート対象外です。v7.0.0でNode 20のサポートが明示的に落とされており、依存パッケージにESM専用のものが含まれます。ランタイムを上げられない案件では6.x系に固定し、上げられる時期が来てから移行する運用にしてください。

関連記事

資料請求

RELATED POSTS 関連記事