Spring for GraphQLは、graphql-javaの実行エンジンをSpringの@Controllerモデルから扱えるようにしたGraphQLサーバー実装です。Spring Boot 4.1ではspring-boot-starter-graphqlとトランスポート用のstarterを組み合わせるだけでエンドポイントが立ち上がり、Micrometerの計装も同梱されています(発行条件は後述します)。本記事では、2026年8月18日時点のMaven Centralと公式ドキュメントで確認した版・設定プロパティをもとに、依存追加からトレース出力までを通しで整理します。Spring Boot 3系から移行するときに名前が変わったプロパティ、そして配布そのものが止まった依存も併せて示します。
まとめ
- Spring Boot 4.1.0が管理するSpring for GraphQLは2.0.4、Spring Boot 3.5.16は1.4.6です。starterを使うなら版指定は不要です。
- 依存は
spring-boot-starter-graphqlだけでは足りません。GraphQLはトランスポート非依存のため、HTTPで公開するならspring-boot-starter-webを併せて入れます。 - スキーマは
src/main/resources/graphql/配下に.graphqlsまたは.gqlsで置けば自動で読み込まれます。 - GraphiQLは既定で無効ですが、スキーマを外部から読み取れるintrospectionは既定で有効です。この2つは別設定なので混同しないでください。
- 可観測性はGraphQL専用のObservationとして
graphql.request、graphql.datafetcher、graphql.dataloaderの3種が用意されています。汎用のHTTPトレースでは見えないフィールド単位の遅延がここで取れます。 - 2024年頃の記事にある
opentelemetry-exporter-jaegerは使えません。OpenTelemetry Java 1.35.0で削除済みで、JaegerへはOTLPで送ります。
以降で、対応バージョンの確認から実装手順、Observationの中身、Spring Boot 4系での送信設定までを順に見ていきます。
Spring for GraphQLの役割と対応バージョン
graphql-javaをSpringのコントローラモデルで包む位置づけ
GraphQLのクエリ解析とスキーマ実行そのものを担うのはgraphql-javaです。Spring for GraphQLはその上位に位置し、AnnotatedControllerConfigurerが@SchemaMapping系のアノテーションが付いたメソッドを走査して、graphql-javaのDataFetcherとして登録します。開発者はSpring MVCのコントローラを書く感覚でリゾルバを実装でき、HTTP・WebSocket・RSocketの各トランスポートはフレームワーク側が用意します。
単体の成果物名はorg.springframework.graphql:spring-graphqlで、Maven Centralでの最新公開版は2.0.4です。ただしSpring Bootから使う場合、この版を自分で指定する必要はありません。GraphQLそのものの設計思想やREST APIとの違いはGraphQLとは?REST APIとの違い・メリット・デメリットをわかりやすく解説で扱っています。
Spring Boot 4.1と3.5で実際に入る依存の版
starterを使うと、Spring Bootのバージョン管理(BOM)が下表の版を解決します。可観測性まわりの版も連動して決まるため、「どのSpring Bootを使うか」がそのまま「どのMicrometer Tracingを使うか」になります。
| Spring Boot | spring-graphql | micrometer-tracing | opentelemetry-bom |
|---|---|---|---|
| 4.1.0 | 2.0.4 | 1.7.0 | 1.62.0 |
| 3.5.16 | 1.4.6 | 1.5.12 | 1.49.0 |
数値はspring-boot-dependenciesの各POMに記載された管理版です。Spring Boot 4系そのものの変更点はSpring Boot 4とは?最新バージョン4.1の変更点・新機能とSpring Boot 3との違いを解説【2026年最新】にまとめています。
最小構成でGraphQLエンドポイントを公開する手順
トランスポート用starterを含む依存の追加
GraphQLはトランスポート非依存の仕様のため、spring-boot-starter-graphqlだけでは通信経路が決まらず、APIを外部に公開できません。公式も同starterを「最低限」と位置づけたうえで、公開手段に応じた追加starterを求めています。選択肢はHTTP向けのspring-boot-starter-web、Servletアプリ向けWebSocketのspring-boot-starter-websocket、HTTPとWebSocketの両方を扱うspring-boot-starter-webflux、TCPとWebSocket向けのspring-boot-starter-rsocketです。一般的なHTTP APIなら次の2つになります。
dependencies {
implementation("org.springframework.boot:spring-boot-starter-graphql")
implementation("org.springframework.boot:spring-boot-starter-web")
}
スキーマファイルの配置先と読み込み範囲
スキーマはspring.graphql.schema.locationsが指す場所から読み込まれ、既定値はclasspath:graphql/**/です。src/main/resources/graphql/配下に拡張子.graphqlsまたは.gqlsのファイルを置けば設定なしで認識されます。拡張子そのものはspring.graphql.schema.file-extensionsで変更できます。
type Query {
bookById(id: ID): Book
}
type Book {
id: ID
name: String
author: Author
}
type Author {
id: ID
firstName: String
lastName: String
}
注意が必要なのは複数モジュール構成です。既定のclasspath:プレフィックスのままだと読み込み範囲が広がらないため、アプリケーションの全モジュールと依存関係からスキーマを集めたい場合は、ディレクトリを変えるのではなくspring.graphql.schema.locationsをclasspath*:graphql/**/とアスタリスク付きのプレフィックスに変更します。
@QueryMappingによるリゾルバ実装
スキーマのQuery型のフィールドは@QueryMapping、任意の型のフィールドは@SchemaMappingで実装します。引数は@Argumentでバインドします。
@Controller
public class BookController {
@QueryMapping
public Book bookById(@Argument Long id) {
// ...
}
@SchemaMapping
public Author author(Book book) {
// ...
}
}
メソッド名がそのままフィールド名、引数に取った親オブジェクトの単純クラス名がそのまま型名の既定値になります。上のauthorメソッドは型「Book」のフィールド「author」に対応します。書き込み系は@MutationMapping、購読は@SubscriptionMappingを使い、この2つと@QueryMappingの3つが@SchemaMappingのメタアノテーションとして、typeNameをQuery・Mutation・Subscriptionに固定したものです。@BatchMappingはこれらとは別系統で、親オブジェクトのリストを受け取って関連データをまとめて読み込むためのものです。例えば書籍のリストに対して著者を一括取得する用途に使います。
GraphiQLの有効化と疎通確認
GraphQLのHTTPエンドポイントはspring.graphql.http.pathの既定値どおり/graphqlに公開されます。ブラウザで試すためのGraphiQL UIは同梱されていますが、spring.graphql.graphiql.enabledの既定値はfalseで、明示的に有効化しないと表示されません。
spring.graphql.graphiql.enabled=true
spring.graphql.schema.printer.enabled=true
有効にするとspring.graphql.graphiql.pathの既定値/graphiqlでUIが開きます。schema.printer.enabledも既定falseで、有効にするとHTTPパスの末尾に/schemaを足したURLで解決済みスキーマを確認できます。スキーマが読み込まれているかを切り分けたいときに役立ちます。
Spring BootでGraphQLを採用する判断基準
エンドポイント増加が止まらない場合に効く条件
GraphQLは「クライアントが必要なフィールドだけを1リクエストで取れる」ことに価値があります。採用が効くのは、画面ごとに必要なフィールドの組み合わせが大きく異なり、REST側でエンドポイントが増え続けている場合です。モバイルとWebで必要な項目が違う、画面改修のたびにBFFを直している、といった状況が典型です。
単一クライアントで見送るべき条件
逆に、単一クライアントしか存在せず、レスポンス形状が安定しているシステムにGraphQLを載せるべきではありません。取得フィールドが可変になることで、キャッシュ設計・認可のフィールド単位の制御・クエリ深度の制限といった、RESTでは不要だった論点が一式増えます。フィールド単位のリゾルバがそれぞれデータソースを叩くため、N+1問題も自前で潰す必要があります。エンドポイント数が問題になっていないなら、RESTのままの方が総コストは低くなります。判断軸をより細かく比べたい場合はREST APIとGraphQLの違いと使い分け|選定基準と運用コストを参照してください。
GraphQL固有の可観測性を担う3種類のObservation
graphql.requestが記録するオペレーション種別と成否
Spring for GraphQLにはMicrometer Observationの計装が直接組み込まれており、GraphQlObservationInstrumentationを登録するとリクエスト単位のObservationがgraphql.requestという名前で作られます。Spring Bootのstarterを使っている場合はこのインストルメンテーションが自動で構成されます。ただしアプリケーションにObservationRegistryが構成されていなければObservationは発行されません。
| KeyValue | カーディナリティ | 内容 |
|---|---|---|
| graphql.operation.type | 低 | query/mutation/subscription(解析失敗時 operation) |
| graphql.outcome | 低 | SUCCESS / REQUEST_ERROR / INTERNAL_ERROR |
| graphql.operation.name | 高 | クライアント指定のオペレーション名 |
| graphql.execution.id | 高 | graphql-java の ExecutionId |
ここで実務上重要なのはgraphql.outcomeの3値の切り分けです。GraphQLはHTTP 200を返しつつレスポンス本体にエラーを含められるため、HTTPステータスだけを見ている監視では失敗を取りこぼします。エラーを含むレスポンスはErrorType.INTERNAL_ERRORが含まれるかどうかでINTERNAL_ERRORとREQUEST_ERRORに分かれるので、アラートはサーバー起因のINTERNAL_ERRORに絞ると、クライアント側の不正クエリによるノイズを避けられます。レスポンスに載ったエラーはObservation Eventとしても記録され、トレース上ではスパンのアノテーションとして扱われるのが一般的です。
graphql.datafetcherとgraphql.dataloaderで追うフィールド単位の遅延
フィールド解決の計測はgraphql.datafetcherという名前で作られます。ここには重要な条件があり、Javaオブジェクトのプロパティを読むだけの「自明な」フェッチは計測対象外です。計測されるのはデータソースへのアクセスを伴う非自明なフェッチだけなので、スパンが大量に増える心配はありません。付与されるのはgraphql.field.name、フィールドのパスを示すgraphql.field.path(例:/bookById)、graphql.outcome、エラー時のクラス名を示すgraphql.error.typeです。ただしここでのgraphql.outcomeはSUCCESSとERRORの2値で、リクエスト単位の3値とは値域が違います。
3つ目のgraphql.dataloaderは、@BatchMappingのコントローラメソッドと、手動登録したDataLoaderインスタンスの呼び出しだけを観測します。graphql.loader.nameと、読み込んだ要素数を示すgraphql.loader.sizeが付きます。この発行条件がそのままN+1の診断手順になります。まず、graphql.datafetcherのスパンは出ているのにgraphql.dataloaderが1件も出ていなければ、その経路は@BatchMappingもDataLoaderも通っておらず、フィールドごとの個別解決のままです。バッチ化の入り口に立てていない状態なので、まずここを疑います。次にgraphql.dataloaderが出ている場合はgraphql.loader.sizeを親の件数と突き合わせ、想定より小さい値に分割されていればバッチ境界が細かすぎると判断できます。いずれもHTTPスパンだけを見ていても分からない情報です。Observationの出力先設定そのものはSpring Boot OpenTelemetry導入|2つのstarterの違いとMaven設定で詳しく扱っています。
Spring Boot 4系のトレース出力設定と廃止済み依存
ZipkinとOTLPで異なるstarterと設定プロパティ
Spring Boot 4.0以降は代表的な2つの組み合わせに専用starterが用意されています。どちらを選ぶかで、追加する依存も設定プロパティの接頭辞も変わります。
| 組み合わせ | starter | 設定プロパティ |
|---|---|---|
| OpenTelemetry + OTLP | spring-boot-starter-opentelemetry | management.opentelemetry.tracing.export.otlp.* |
| Brave + Zipkin | spring-boot-starter-zipkin | management.tracing.export.zipkin.* |
サンプリング率はmanagement.tracing.sampling.probabilityで調整します。既定値は0.1で、全リクエストの10%しか送信されません。開発中にトレースが見当たらない原因の多くはこれです。OpenTelemetryを使う場合はサンプラー自体もmanagement.opentelemetry.tracing.samplerで選べます。実務で明示指定する場面はほぼ検証時に限られ、そこで使うのは全件収集のalways-onです。既定は親スパンの判定を引き継ぐparent-based-trace-id-ratioで、残るalways-off、trace-id-ratio、parent-based-always-on、parent-based-always-offは、親子関係と比率のどちらを優先するかを細かく制御したい場合の選択肢です。
management.tracing.sampling.probability=1.0
management.tracing.export.zipkin.endpoint=http://localhost:9411/api/v2/spans
2024年以前の記事に残る廃止済み依存の見分け方
Spring BootとOpenTelemetryはこの2年で構成が入れ替わっており、当時の手順をそのまま貼ると依存解決の段階で失敗します。扱いが変わったのは次の依存とプロパティです。
io.opentelemetry:opentelemetry-exporter-jaegerはOpenTelemetry Java 1.35.0(2024年2月9日)で削除されました。Maven Centralでの最終公開版は1.34.1です。JaegerはOTLPを直接受け取れるため、現在はOTLPエクスポータを使います。- Zipkinへの送信先プロパティは、Spring Boot 3系の
management.zipkin.tracing.endpointからSpring Boot 4系ではmanagement.tracing.export.zipkin.endpointに変わりました。既定値はどちらもhttp://localhost:9411/api/v2/spansです。 - OpenTelemetryはZipkinサポートを非推奨としており、Spring Boot側の自動設定も4.2で削除予定と公式ドキュメントに明記されています。OpenTelemetryとZipkinを組み合わせている構成は、Braveへ切り替えるかOTLP経由に寄せる前提で計画してください。
見分け方は単純で、手元の依存をMaven Centralのmaven-metadata.xmlで引き、最新公開版が数年前で止まっていれば配布終了と判断できます。OpenTelemetry本体の構成要素についてはOpenTelemetry(OTel)とは|3つのシグナル・OTLP・Collector・Prometheusとの違いを解説で整理しています。
よくある質問
Spring Boot 3系のままでもSpring for GraphQLの可観測性は使えますか?
使えます。Spring Boot 3.5.16が管理するSpring for GraphQLは1.4.6で、Micrometer Tracingは1.5.12です。graphql.requestやgraphql.datafetcherといったObservationの計装はSpring for GraphQL側に組み込まれているため、3系でも利用できます。違いが出るのはトレース出力側です。Spring Boot 4.0で導入されたspring-boot-starter-zipkinやspring-boot-starter-opentelemetryは3系に存在せず、公式が案内するOpenTelemetryとZipkinの組み合わせはspring-boot-starter-actuator、micrometer-tracing-bridge-otel、opentelemetry-exporter-zipkinを個別に追加する形です。プロパティもmanagement.zipkin.tracing.*のままです。
GraphiQLを無効にすればスキーマは外部から見えなくなりますか?
なりません。ここは取り違えやすい部分です。スキーマの型定義を外部から読み取れるのはintrospectionという機能で、これはGraphiQLの有効・無効とは独立して既定で有効です。GraphiQL自体はそれを利用するツールにすぎません。型定義を公開したくない場合はspring.graphql.schema.introspection.enabledをfalseに設定します。GraphiQLを既定のfalseのままにしていても、introspectionを閉じていなければスキーマは読み取れる、と理解してください。
Observationが1件も記録されないときは何を確認すればよいですか?
まずObservationRegistryが構成されているかを見てください。これが無いとObservationは発行されません。次にサンプリング率で、management.tracing.sampling.probabilityの既定値0.1のままだと10%しか送信されないため、検証時は1.0にします。それでも特定のフィールドだけ出ない場合は計測対象の条件を疑ってください。graphql.datafetcherは非自明なフェッチだけが対象で、Javaオブジェクトのプロパティを返すだけのフィールドにはスパンが作られません。
トレースをJaegerで確認したい場合はどの依存を入れますか?
spring-boot-starter-opentelemetryを追加し、management.opentelemetry.tracing.export.otlp.endpointにJaegerのOTLP受信エンドポイントを指定します。JaegerはOTLPをネイティブに受け取れるため、専用のエクスポータは不要です。旧来のopentelemetry-exporter-jaegerはOpenTelemetry Java 1.35.0で削除済みで、依存に書いても解決できません。
既存のREST APIと同じアプリケーションにGraphQLを追加できますか?
追加できます。GraphQLをHTTPで公開する際に使うspring-boot-starter-webはSpring MVCの実装そのものなので、同じアプリケーション内で@RestControllerによるRESTエンドポイントと、spring.graphql.http.path(既定/graphql)のGraphQLエンドポイントが併存します。既存のREST APIを止めずに、必要な画面から順にGraphQLへ寄せる段階移行が取れるということです。RESTを一度に置き換える必要はありません。