既に1年近く前の話ですが、愛用していたGraphQL Spring Boot Startersがアーカイブになってしまいました。Spring for GraphQLの登場で予想はしていましたが、まだ機能面で不満があったので残念です。
しかし、時代は待ってくれませんし、Spring for GraphQLでのDataLoaderの実装(特に@BatchMapping)は簡単になりますし、Spring Frameworkなので今後の進歩も期待できるので、移行は大歓迎でもあります。
不満
不満とは、スキーマと実装に不一致があったときの挙動で
- GraphQL Spring Boot Starters
2024-10-12T12:34:02.149+09:00 ERROR 224838 --- [template-project] [ restartedMain] o.s.boot.SpringApplication : Application run failed
org.springframework.context.ApplicationContextException: Unable to start web server
...中略...
Caused by: graphql.kickstart.tools.resolver.FieldResolverError: No method or field found as defined in schema <unknown>:14 with any of the following signatures (with or without one of [interface graphql.schema.DataFetchingEnvironment, class graphql.GraphQLContext] as the last argument), in priority order:
example.domain.AccountModel.address()
example.domain.AccountModel.getAddress()
example.domain.AccountModel.address
- Spring for GraphQL
2024-10-12T12:29:03.484+09:00 INFO 220161 --- [template-project] [ main] o.s.b.a.g.GraphQlAutoConfiguration : GraphQL schema inspection:
Unmapped fields: {AccountModel=[address]}
Unmapped registrations: {}
Unmapped arguments: {}
Skipped types: []
GraphQL Spring Boot Startersはエラーが発生してSpring Bootの起動が停止するのに対し、Spring for GraphQLはログに出るものの、そのままSpring Bootは起動してしまいます。そして、当然ながら実行中には実装の不一致に伴うエラーが発生するわけです。
このSpring for GraphQLの挙動は大規模開発とかでは便利なんですかね?巨大なスキーマがあって、複数のアプリケーションで実装を分担するみたいな。
でも自分だとほぼ1人で完結する程度の開発なので、スキーマの一部をワザと実装しないということもないし、実装漏れには確実に気づきたいわけです。
解決策
まずは結論の実装コードから。
@Configuration
class GraphQlConfig {
@Bean
@ConditionalOnProperty(prefix = "spring.graphql.schema.inspection", name = ["stop-on-unmapped"], havingValue = "true", matchIfMissing = false)
fun graphQlSourceBuilderReportConsumerCustomizer(): GraphQlSourceBuilderCustomizer {
return GraphQlSourceBuilderCustomizer{ builder: GraphQlSource.SchemaResourceBuilder ->
builder.inspectSchemaMappings { report ->
check(report.unmappedArguments().isEmpty()
&& report.unmappedFields().isEmpty()
&& report.unmappedRegistrations().isEmpty()
&& report.skippedTypes().isEmpty()) {
report.toString()
}
}
}
}
}
Spring for GraphQLの自動設定だと、GraphQlAutoConfiguration#graphQlSourceの中で、GraphQlSource.SchemaResourceBuilder#inspectSchemaMappingsメソッドにログオブジェクトのinfoメソッドを渡しています。これによりGraphQlSource.SchemaResourceBuilder#initGraphQlSchemaの中でSchemaReportが生成され、ログとして出力されていました。
そこで自作のGraphQlSourceBuilderCustomizerにより、
GraphQlSource.SchemaResourceBuilder#inspectSchemaMappingsメソッドに渡すオブジェクトを上書きし、SchemaReportで不一致を検出したら例外を発生させるようにしました。
また、この挙動をプロパティで制御できるようにもしました。
これにより不一致がある場合は以下のような出力でSpring Bootの起動が停止するようになりました。
2024-10-12T13:30:59.211+09:00 ERROR 255549 --- [template-project] [ main] o.s.boot.SpringApplication : Application run failed
...中略...
Caused by: java.lang.IllegalStateException: GraphQL schema inspection:
Unmapped fields: {AccountModel=[address]}
Unmapped registrations: {}
Unmapped arguments: {}
Skipped types: []
まとめ
この他にも、Spring for GraphQLへ移行するにあたって、セキュリティやエラー処理など、いろいろと検証していきます。