How to skip validation rules in GraphQL Spring Boot application

How to skip validation rules in GraphQL Spring Boot application


0

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

  • What types are Type1Detail and Type2Detail? The two detail 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.

    – David Maze

    Nov 26, 2022 at 12:59

2 Answers
2


0

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));
            }
        };
    }


0

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));
        }
    };
}



Leave a Reply

Your email address will not be published. Required fields are marked *