graphql-codegenのplugin機構
gpt-5.icon
graphql-codegen の plugin 機構は、かなり直球に言うと「GraphQL の schema / documents を入力にして、任意のコードやテキストを生成する拡張ポイント」です。公式ドキュメントでも、Code Generator は GraphQL の AST を解析し、その結果に対して plugin が処理して最終出力を返す仕組みだと説明されています。plugin には GraphQLSchema、documents、ユーザー設定が渡され、plugin 側は string または Promise<string> を返します。複数 plugin を並べると、その出力が結合されて 1 つの生成ファイルになります。 (The Guild)
どういうものか
典型的な設定はこうです。
code:ts
import type { CodegenConfig } from '@graphql-codegen/cli'
const config: CodegenConfig = {
schema: 'http://localhost:4000/graphql',
documents: 'src/**/*.graphql',
generates: {
'./src/generated.ts': {
plugins: [
'typescript',
'typescript-operations',
'typed-document-node',
],
},
},
}
export default config
このとき plugins に並んでいるそれぞれが plugin です。
たとえば:
typescript は schema から基本型を生成
typescript-operations は query / mutation / fragment の型を生成
typed-document-node は実行可能な TypedDocumentNode を生成
という役割分担です。実際、公式 docs でも typed-document-node は typescript と typescript-operations と組み合わせて使う例が出ています。 (The Guild)
つまり plugin 機構は、1つの大きな generator ではなく、小さな generator を合成する設計です。
plugin と preset の違い
ここは混乱しやすいです。
plugin: 実際にコードを出力する単位
preset: 複数 plugin の組み合わせ方や出力ファイル構成をまとめたもの
最近の公式 docs では、クライアント用途では client preset が推奨されています。これは plugin を内部で束ねて、「どこに何を出すか」まで面倒を見てくれます。 (The Guild)
なので実務では:
まずは preset を使う
足りないところだけ plugin を直接触る
さらに特殊なら custom plugin を書く
という順で考えると分かりやすいです。
どういうユースケースがあるか
大きく 3 パターンあります。
1. 既存 plugin を組み合わせる
いちばん普通です。
例:
schema 型を生成する
operations 型を生成する
resolver signature を生成する
introspection JSON を生成する
独自の import 文やヘッダコメントを足す
公式の plugin 一覧にもかなり多くの plugin があり、TypeScript 系、resolvers、schema-ast、introspection などが揃っています。 (The Guild)
2. 既存 plugin では出せない“社内向けフォーマット”を出す
custom plugin が効くのはここです。
例:
社内 SDK の wrapper を自動生成
operation ごとの API クライアント関数を自動生成
バックエンド向けに監査用メタ情報を吐く
schema から社内ドキュメント Markdown を生成
enum だけ別形式で吐く
schema の命名規約違反レポートを生成
公式 docs でも、custom plugin は「新しい言語テンプレート」「既存 plugin のカスタマイズ」「出力ファイルへの独自コンテキスト追加」に有用とされています。 (The Guild)
3. build pipeline の一部として使う
Codegen を単なる型生成ではなく、GraphQL を single source of truth にしたコード生成基盤として使うケースです。
例:
GraphQL schema から OpenAPI ライクな補助情報を生成
フロント用・BFF 用・モック用のファイルを同時生成
CI で schema チェック結果をテキスト出力
operation 一覧からテスト雛形を生成
custom plugin はどう自分で用意するか
最小形はかなりシンプルです。
公式 docs ベースだと、plugin はだいたいこういう形です。 (The Guild)
code:ts
import type { Types } from '@graphql-codegen/plugin-helpers'
import type { GraphQLSchema } from 'graphql'
export const plugin: Types.PluginFunction<MyPluginConfig> = async (
schema: GraphQLSchema,
documents,
config
) => {
const typeMap = schema.getTypeMap()
const typeNames = Object.keys(typeMap)
return [
'// Generated by my plugin',
'export const allTypeNames = ' + JSON.stringify(typeNames, null, 2),
].join('\n')
}
これを package にして、Codegen 側で読み込ませます。
code:ts
import type { CodegenConfig } from '@graphql-codegen/cli'
const config: CodegenConfig = {
schema: './schema.graphql',
generates: {
'./src/my-generated.ts': {
plugins: [
{ './codegen/my-plugin.js': {} }
]
}
}
}
export default config
plugin が受け取るもの
custom plugin が受け取る主な入力は次の 3 つです。
schema: 統合済みの GraphQLSchema
documents: query / mutation / fragment などの document 群
config: codegen.ts で plugin に渡した設定
schema は root / output level で指定した複数 schema がマージされて 1 つの GraphQLSchema として渡されます。documents も scanner によって .graphql や .ts/.tsx から集められます。 (The Guild)
もう少し実践的な plugin 例
たとえば「operation ごとに API 関数を吐きたい」とします。
入力:
code:graphql
query GetUser($id: ID!) {
user(id: $id) {
id
name
}
}
custom plugin で documents を見て、各 OperationDefinition を走査し、こういう出力を返すイメージです。
code:ts
export async function getUser(variables: GetUserQueryVariables) {
return client.request(GetUserDocument, variables)
}
これは既存 plugin でも近いことはできますが、
関数命名規約が特殊
社内 client を使いたい
例外処理や logging を埋め込みたい
キャッシュキー生成も一緒に出したい
みたいなときは custom plugin のほうが楽です。
plugin を書くときの考え方
custom plugin を書く前に、まずこの順で考えると失敗しにくいです。
1. preset で済まないか
2. 既存 plugin の組み合わせで済まないか
3. add plugin などの軽い補助で済まないか
4. それでも無理なら custom plugin
plugin を自作すると、将来の Codegen 本体更新に追従するコストが出ます。
一方で、出力仕様が社内固有ならとても相性が良いです。