Skip to content

server_migrate_1 x_to_2 x

etienne-sf edited this page Feb 27, 2023 · 22 revisions

Work in progress (2.x is about to be released)

Introduction

This page describe how to migrate a server implementation from a 1.x version to the last 2.x version.

If you're looking for a migration of the client implementation, please check the Client migration from 1.x to 2.x

The 2.x version is based on spring-graphql. Spring and spring-graphql are responsible for the whole transport part. The first versions are a mostly a port to use spring-graphql, with the lower impact possible on your exiting code.

Next versions will allow more control on the spring-graphql part, including overriding the Spring GraphQL controllers.

The dependencies are based on Spring MVC.

TODO :

  • In 1.x releases of the plugin the GraphQL endpoint's path is configured by the graphql.url entry in the Spring configuration file (application.yml or application.properties)
    • [for servers only] In the 2.x releases, this configuration is manager by spring. So the relevant configuration entry is spring.graphql.path in the Spring configuration file. Its default value is /graphql

TODO Finish here

Changes that impact the existing code

TODO None of them??

Other changes

Exception management:

For Queries and Mutations

Like stated in the spring-graphql project, you can register a DataFetcherExceptionResolverAdapter that allows proper Exception management in your GraphQL server. There is a sample in the provided graphql-maven-plugin-samples-allGraphQLCases-server sample module. You just have to create a subclass of DataFetcherExceptionResolverAdapter, and override one of the resolveToSingleError or resolveToMultipleErrors methods.

Don't forget to add the @Component annotation, to register it.

For Subscription

It is undocumented, but the same mecanism exists for subscriptions exceptions. The class to override is SubscriptionExceptionResolverAdapter.

Server endpoint

The default endpoint is /graphql (like before). The way to change it is now the spring.graphql.path property (in application.properties or application.yml), like this:

spring.graphql.path=/another-graphql-endpoint-path

Support of WebSocket (mandatory for subscriptions)

The web socket transport is mandatory for subscription. To allow it, a line like this must be added in the application.properties (or application.yml ) file:

spring.graphql.websocket.path=/graphql

Other changes, for information

Schema files at runtime

The schema files are copied to the default spring-graphql folder, that is: the ./graphql folder in the classpath

Allow the RSocket protocol

RSocket is also supported (see the spring-graphql doc for more information on how to do this)

Internal wiring of the Data Fetchers

The main change between the original graphql-java implementation and the Spring logic is the way to wire the Data Fetchers to the GraphQL engine.

  • In 1.x branch (with graphql-java), this was done in the generated GraphQLWiring class. It used to wire the code for custom scalars and all GraphQL type, unions... Then, the GraphQLDataFetchers class would wire each DataFetcherDelegateXxxx method to the relevant non-scalar field.
  • spring-graphql uses a different logic: * The GraphQLWiring spring component still exists. But it only wires the custom scalar implementations. This are declared in the plugin configuration, like in 1.x releases. * The Controllers: GraphQL web applications must declare a Controller for each GraphQL object, union... This is done by classes XxxxController that are generated by the plugin. These Controllers are responsible for managing each non-scalar field. * To keep compatibility with code written for 1.x releases of the plugin, the plugin generates these XxxxController classes. They are responsible for calling the DataFetchersDelegateXxxx components (including the BatchLoaders), and then reuse the code written for the 1.x release * You can also write the XxxxController yourself (see below). But then, you must write yourself the controllers, and maintain them when the GraphQl schema changes.

TODO : re-write this part

  • It's no more possible to override the GraphQLDataFetchers spring component.
    • If the default data fetchers doesn't respect your needs, check the Spring Controllers
  • Exception may be managed by one or more DataFetcherExceptionResolver in a list of graphqlGraphQLError
    • See sample: CustomExceptionHandling and its registration in CustomExceptionHandlingRegistring
  • GraphiQL is active by default in spring-graphql, at /graphiql. It can be turned off with the spring.graphql.graphiql.enabled property
  • The spring.graphql.schema.printer.enabled property allows to expose the schema file in text format at /graphql/schema
  • Removed these dependencies :
    • For server mode:
      • activation and spring-boot-starter: they are no more directly needd.
      • graphql-java-spring-mvc (the applications are now web flux apps)
      • spring-boot-starter-websocket (the Web Socket protocol is managed directly by spring-boot-starter-graphql)
    • For client mode:
      • TODO: document it

Step to remove the DataFetchers

If you created a GraphQL server, based on 1.* versions of the plugin, the only link with the plugin's generated code is the DataFetcherDelegate implementations. You can keep it as it was with 1.* versions of the plugin: it will still work as before.

You can also broke this link, and change your DataFetcherDelegate implementations to Spring controllers. This allows to use more powerful functionalities.

The steps to do this are:

  • TODO: plugin parameter to not generate the dataFetcherDelegate (and the XxxxController classes)
  • There is a work for each of your DataFetcherDelegate classes:
    • You should rename it, as the naming convention in spring-graphql is XxxController, where Xxx is the name of the relevant GraphQL type (as defined in your GraphQL schema)
    • Mark it with the @Controller Spring annotation (otherwise, it is just ignored by Spring)
    • For DataFetcherDelegate classes that are not the Query, Mutation or Subscription one: mark it with either
      • @SchemaMapping (in which case the XxxController naming convention should be obeyed, but you also need to check the @SchemaMapping on the methods)
      • @SchemaMapping(typeName = "Xxx") where Xxxx is the GraphQL type's name
    • For classes that contain a batchLoader or a unorderedReturnBatchLoader, you must register a DataLoader
      • TODO : Document howto register a DataLoader (or to use the @BatchMapping Spring annotation)
    • TODO finish here
  • Mark all your

Current step

TODO: §4.1.1: find schema files

GraphQLServerMain

No more bean declaration. This class now only contains the main method.

CustomGraphQLController

This class is no longer used (along with all the graphql-java-spring modules)

RuntimeWiring

The existing code generates a GraphQLWiring class, with various method that could be overriden to change the default runtime wiring.

spring-graphql introduces a new notion: RuntimeWiringConfigurer. Spring Boot will find all Spring bean that implements the RuntimeWiringConfigurer interface, and call their configure() method.

The main update are:

  • The GraphQLWiring class implements the RuntimeWiringConfigurer class.
  • The only use of this class is to declare the scalar implementations.
  • DataFetchers are now found by Spring Boot, by scanning the @Controller annotation
  • TODO : and TypeResolvers?

TODO: Check if GraphQLWiring can be overriden Probably not. So how can one override the default type resolving stuff. (first step: check what was ask on this subject, that is: the reason why this has been structured this way. And does a test exist for this). See CustomGraphQLWiring

TODO explain CustomGraphQLWiring

DataLoader

TODO: finish the job

  • Use the @BatchMapping on a default interface method
  • "By default, the field name defaults to the method name, while the type name defaults to the simple class name of the input List element type. Both can be customized through annotation attributes."
    • To manage cases where the method name is not the field name (when a field name doesn't respect the java naming convention), then the actual field's name should be written in the @BatchMapping annotation

TypeResolver

The ClassNameTypeResolver is the default TypeResolver for interfaces and unions. It tries to match the simple class name of the value to a GraphQL Object Type and if it is not successful, it also navigates its super types including base classes and interfaces, looking for a match.

In the 1.* release, the TypeResolvers are defined in the GraphQLWiring generated class. This allows to manage GraphQL Object Type names that dosn't match Java naming convention (for instance, when using Java keywords). But this would be quite unlikey to happen. It works well when the DataFethers signatures are generated by the code: their return is directly managed by the plugin's code.

On the other side, with spring-graphql, it is possible to creates one's own DataFetcher that may return java datatype that are not match as well with GraphQL Object Type's name. In this case, the Spring Boot implementation would be better.

TODO: one of the above solutions. 2 seems the best one. TODO: 1) add a parameter to generate them ?? TODO: 2) no more definition of TypeResolver in the generated code, and add a doc to explain how to do it when it is necessary. TODO: This allows to entirely remove the GraphQLWiring class, and so let's the developper free to do what he/she wants there. TODO: 3) possibly just generate it when the java name is different from the GraphQL Object Type's name (but how to allow the developper to override it?).

Clone this wiki locally