I have a SpringBoot + GraphQL application. I am trying to upgrade to the latest version (graphql-spring-boot-starter 11.1.0 -> 13.0.1) which changes graphql-java
from 16.2 -> 19.2.
I have schema that looks like
enum Type {
TYPE1
TYPE2
}
interface Generic {
name: String
type: Type
}
type Type1 extends Generic {
name: String
type: Type
detail: Type1Detail
}
type Type2 extends Generic {
name: String
type: Type
detail: Type2Detail
}
and my queries have pattern like this:
query {
GetObject {
name
type
... on Type1 {
detail
}
... on Type2 {
detail
}
}
This has been working for the past few years on 16.2 and earlier, but with the updated version, I am getting error that looks like
Validation error (FieldsConflict@[...] : detail : returns different types 'Type1Detail' and 'Type2Detail'
Is there any way to fix it other than changing the schema? Because I have followed this naming pattern in a lot of places with several types that is hard to change now.
Alternately, I was trying Skipping Validation Rules introduced in v18.0, but I am not able to find what bean (and how) to create to override the GraphQLContext
to pass in the specific predicate to disable that check.
1
2 Answers
I was able to solve this by creating GraphQLServletContextBuilder
bean like below. Other beans like GraphQLContextBuilder
did not help.
@Bean
GraphQLServletContextBuilder graphQLServletContextBuilder() {
return new GraphQLServletContextBuilder() {
final Predicate<Class<?>> predicate =
aClass -> !aClass.equals(OverlappingFieldsCanBeMerged.class);
@Override
public GraphQLKickstartContext build(
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) {
// I don't know if all 3 are required, but returning
// null was throwing NPE.
return GraphQLKickstartContext.of(Map.of(
HttpServletRequest.class,
httpServletRequest,
HttpServletResponse.class,
httpServletResponse,
"graphql.ParseAndValidate.Predicate",
predicate));
}
@Override
public GraphQLKickstartContext build(
Session session, HandshakeRequest handshakeRequest) {
// I don't know if all 3 are required, but returning
// null was throwing NPE.
return GraphQLKickstartContext.of(Map.of(
Session.class,
session,
HandshakeRequest.class,
handshakeRequest,
"graphql.ParseAndValidate.Predicate",
predicate));
}
@Override
public GraphQLKickstartContext build() {
return GraphQLKickstartContext.of(
Map.of("graphql.ParseAndValidate.Predicate", predicate));
}
};
}
Great answer from Sailesh, I improved it slightly to use the default’s super methods and add my own predicate instead of overriding everything. I am using the Immutable library to help me out here, but can be easily done with plain java.
@Bean
public GraphQLServletContextBuilder graphQLServletContextBuilder() {
return new DefaultGraphQLServletContextBuilder() {
private final String key = "graphql.ParseAndValidate.Predicate";
private final Predicate<Class<?>> predicate =
aClass -> !aClass.equals(FieldsOnCorrectType.class);
@Override
public GraphQLKickstartContext build(
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) {
return GraphQLKickstartContext.of(
ImmutableMap.builder().putAll(super.build(httpServletRequest, httpServletResponse).getMapOfContext())
.put(key, predicate).build());
}
@Override
public GraphQLKickstartContext build(
Session session, HandshakeRequest handshakeRequest) {
return GraphQLKickstartContext.of(
ImmutableMap.builder().putAll(super.build(session, handshakeRequest).getMapOfContext())
.put(key, predicate).build());
}
@Override
public GraphQLKickstartContext build() {
return GraphQLKickstartContext.of(
Map.of(key, predicate));
}
};
}
What types are
Type1Detail
andType2Detail
? The twodetail
fields don't specifically need to be the same type, but if they're object types, they must select fields with the same type, and if they're scalars, they can't be different types. Full details in Field Selection Merging in the GraphQL spec. If your query is actually invalid, you may need to modify it (maybe using aliases) rather than working around it in client code.Nov 26, 2022 at 12:59