15.0.0 is a major release. It moves to Java 17, graphql-java 26 and Jackson 3, removes the options deprecated in 14.x, and turns most of the schema warnings added in 14.x into build errors. Check your 14.x startup logs for WARNs from graphql.kickstart.tools before upgrading, since most of them fail the build now.
Requirements
- Java 17 is the minimum (#923).
- graphql-java 26 is the minimum (#919). With graphql-java 25, a schema that applies any directive fails to build with a
NoSuchFieldError. - Jackson 3 (3.2.4,
tools.jackson) instead of Jackson 2 (#925, #810). No Jackson 2 support is kept.jackson-datatype-jdk8isn't needed anymore,Optionalandjava.timeare built into Jackson 3. - graphql-java-tools no longer brings in
org.antlr:antlr4-runtime(#927). An app that uses antlr directly needs to declare it. - ⚠️ graphql-spring-boot doesn't work with 15.0.0. Its
GraphQLJavaToolsAutoConfigurationcallsSchemaParserDictionary.getDictionary()and builds a Jackson 2PerFieldObjectMapperProvider, both of which are gone. The project has been archived since December 2023, so there won't be a release that fixes it. Stay on 14.x, move to Spring for GraphQL, or build theSchemaParserbean yourself.
Breaking API changes
- Jackson 3 mappers (#925). Jackson 3 mappers are immutable, so
ObjectMapperConfigurergets theJsonMapper.Builderinstead of the mapper:A module added there replaces the library's module of the same type, e.g. a// before .objectMapperConfigurer { mapper, _ -> mapper.registerModule(MyModule()).disable(FAIL_ON_UNKNOWN_PROPERTIES) } // after .objectMapperConfigurer { builder, _ -> builder.addModule(MyModule()).disable(FAIL_ON_UNKNOWN_PROPERTIES) }
KotlinModulewith other features.PerFieldObjectMapperProviderreturns a Jackson 3ObjectMapperand is afun interfacenow.PerFieldConfiguringObjectMapperProvideris removed, useobjectMapperConfigurerinstead. The Kotlin(ObjectMapper, ObjectMapperConfigurerContext) -> Unitoverload is removed too, a Kotlin lambda converts toObjectMapperConfigureron its own.- A resolver that can't convert an argument fails with Jackson's own exception (e.g.
MismatchedInputException) rather than anIllegalArgumentExceptionwrapping it.
introspectionEnabledis removed (#912, #858), deprecated in 14.5.0. Turn introspection off with graphql-java instead:An introspection query then fails with an "introspection disabled" error when it runs, instead of the// before SchemaParserOptions.newOptions().introspectionEnabled(false) // after, for the whole JVM Introspection.enabledJvmWide(false) // after, per request ExecutionInput.newExecutionInput() .query(query) .graphQLContext(mapOf(Introspection.INTROSPECTION_DISABLED to true))
FieldUndefinedvalidation error it got before.__typenamekeeps working.missingResolverDataFetcher(...)is removed (#915, #846). Replace.missingResolverDataFetcher(fetcher)with.missingResolverDataFetcherProvider { _, _ -> fetcher }.SchemaParserDictionary.getDictionary()andgraphql.kickstart.tools.util.BiMapare removed (#908, #900). Usedictionary.toMap(), which exists since 14.4.0.SchemaObjects(#922, #917):dictionaryis aSet<GraphQLNamedType>instead of aSet<GraphQLType>. Code that builds or copiesSchemaObjectswith aSet<GraphQLType>stops compiling:// before schemaObjects.copy(dictionary = types) // types: Set<GraphQLType> // after schemaObjects.copy(dictionary = types.mapTo(linkedSetOf()) { it as GraphQLNamedType })
- The
copyoverload withoutschemaAppliedDirectives, hidden since that parameter was added, is removed. Code compiled against a version before it needs a recompile.
InvalidSchemaErroris removed (#927). Nothing has thrown it since graphql-java 19, so acatchfor it can just be deleted.
Schemas that now fail to build
Each of these logged a WARN in 14.x and throws at build now.
- Invalid applied directives (#914, #884): a directive applied where its definition doesn't allow it, without one of its required arguments, or more than once when it isn't
repeatable(also across a type and its extensions). TheSchemaErrorlists every such directive with its line. Move the directive, pass the argument (or give it a default), or declare the directiverepeatable. - Invalid directives on scalars (#909, #893): a directive with no definition, or an argument its definition doesn't have. 14.x logged
Scalar '<name>' is built without its directives from the schemaand dropped all of the scalar's directives. Declare the directive, fix the argument name or remove it. To keep an undeclared directive, setallowUndeclaredDirectives(true). - Resolver methods missing a parameter for an argument (#911, #889), when they take a
DataFetchingEnvironment,GraphQLContextor thecontextClassin that argument's place. Add a parameter for each argument, in order, before the environment or context parameter. On 14.x, search your build logs forhas no parameter for its argumentto find all of them. - Resolvers that are JDK dynamic proxies (#913, #868), for example Spring AOP without
proxyTargetClass = true. TheFieldResolverErrornames every field that can't be called, even if it's never queried. Use class-based proxies (proxyTargetClass = truein Spring), and add a getter for a public field. - Repeated declarations (#916, #863, #865): a type and its extensions, or a single object or input definition, can't declare the same enum value, interface field, union member, object field or input field twice. 14.x kept the first one for enums, interfaces and unions and the last one for objects and input objects. Now the build throws a
SchemaErrorlikeType 'Color' declares value 'RED' more than once at line 6, column 5.Remove the repeat, since an extension only adds what the type doesn't have.
Behaviour changes
- Legacy context fallbacks are removed (#915, #846), deprecated with a WARN since 14.2.0:
- A custom context class that is only passed as the context object (
ExecutionInput.context(...)) is no longer given to resolvers. A resolver with a parameter of that class fails the field with an error saying so. Put it in theGraphQLContextunder its class instead, e.g.graphQLContext(mapOf(MyContext::class.java to myContext)). - ⚠️ A
CoroutineScopethat is only passed as the context object is no longer used by suspend resolvers, and nothing is logged or fails. They run inCoroutineScope(Dispatchers.Default), so cancelling your scope no longer cancels them. Put it in theGraphQLContextunder theCoroutineScopeclass key instead, e.g.graphQLContext(mapOf(CoroutineScope::class.java to scope)).
- A custom context class that is only passed as the context object (
- Jackson 3 defaults (#925). The library's own mapper keeps two of the Jackson 2 defaults, since the new ones silently lose or change a client's data: an input field the class has no property for fails the request, and enums convert by
name(). Other new defaults apply:- an explicit
nullinto a primitive input field (e.g.count: Intbound toint) fails instead of giving0. An omitted or null argument bound to a primitive parameter still gets the Java default. java.timevalues convert natively and are written as ISO strings, e.g. aLocalDatescalar inside anObjectNodeorMapargument arrives as"2020-01-02".- an input field that none of a class's
@JsonUnwrappedproperties know fails the request, and is reported at build, instead of being dropped. - the messages say
Unrecognized propertyinstead ofUnrecognized field. - with your own mapper (
objectMapperProvider), all of Jackson 3's defaults apply. Spring's and plain Jackson 3 mappers drop unknown input fields, and the unmapped input field warning says so.
- an explicit
- When no
objectMapperConfigureris set, all fields share one mapper instead of building one each (#925). - Input object field order (#916): input objects list the fields of the type first, then those of each extension in order. The extensions' fields used to come first, so code that reads
GraphQLInputObjectType.getFieldDefinitions()by position sees the new order. - Nested return values are unwrapped the way the schema build matched them (#918, #905):
- a generic wrapper that only a custom
schemaWrapperintroduces is now unwrapped below the outermost value like any other. - custom scalars get the unwrapped values of nested wrappers and futures, e.g.
[DateTime]fromList<CompletableFuture<OffsetDateTime>>gets theOffsetDateTimes. ACoercingthat unwrapped them itself now only sees the plain values. - a
Futurethat isn't aCompletionStage, nested in a list or another wrapper or returned by a nested wrapper's transformer (unless the return type ends inObject), now fails the field with "graphql-java can't wait on". If yourValueUnboxercalledget()on it, return aCompletableFutureinstead. - a nested generic wrapper is unwrapped with the wrapper registered for its declared type, not the most specific one its value is an instance of. If you registered a subclass as a wrapper of its own, declare that subclass in the return type.
- a future directly inside another wrapper (
Try<CompletableFuture<X>>) now goes through the transformer of a wrapper you registered for its type, which used to be skipped there. With the default future wrappers it still reaches graphql-java as the future the resolver returned. If you need the resolver's own future there, declare it as a type you didn't register a wrapper for (e.g.Try<CompletionStage<X>>) or make the transformer return the future it gets.
- a generic wrapper that only a custom
- Custom scalars without visible properties that Jackson writes with a type id (
@JsonTypeInfo) or an object id (@JsonIdentityInfo) now fail with an error instead of reaching the resolver as a default instance (#934, #932). InObjectNodearguments they're passed with their value. - ⚠️ A return type that uses
DataFetcherResultorOptionaltwice for one value, likeOptional<DataFetcherResult<Optional<T>>>orDataFetcherResult<Optional<DataFetcherResult<T>>>, now fails the build (#940, #939). These types built before but gave graphql-java the wrong value. 14.x logged nothing for them.
New features
GenericWrapper.withCompletionStageTransformerregisters a wrapper whose transformer returns a future of its content, such as Reactor'sMono::toFuture(#941, #938). A future declared inside it, likeMono<CompletableFuture<Item>>, is then waited on too. Wrappers registered withwithTransformerkeep their behaviour.
Bug fixes
-
A resolver can return
Optional<DataFetcherResult<T>>(#940, #939). Before, graphql-java got theDataFetcherResultas the value. -
Custom scalars backed by a Kotlin class without visible properties are passed to resolvers with their value, in input objects,
Optionalparameters andObjectNodearguments (#931, #926). -
An application on the module path no longer needs Spring AOP when it doesn't use it (#930).
What's Changed
- Version Release by @github-actions[bot] in #921
- Unwrap nested return values the way the schema build matched them by @oryan-block in #918
- Remove BiMap and SchemaParserDictionary.getDictionary by @oryan-block in #908
- Throw for invalid directives on scalars by @oryan-block in #909
- Drop graphql-java 25 support by @oryan-block in #919
- Throw when a resolver method is missing a parameter for an argument by @oryan-block in #911
- Reject fields, values and members that a type and its extensions declare twice by @oryan-block in #916
- Remove the introspectionEnabled option by @oryan-block in #912
- Throw at build for resolvers that can't be called on their JDK dynamic proxy by @oryan-block in #913
- Remove missingResolverDataFetcher and the legacy context fallbacks by @oryan-block in #915
- Reject invalid applied directives with a SchemaError by @oryan-block in #914
- Make SchemaObjects.dictionary a Set by @oryan-block in #922
- Remove InvalidSchemaError and the antlr4-runtime dependency by @oryan-block in #927
- Migrate Renovate config by @renovate[bot] in #924
- Require Java 17 by @oryan-block in #923
- Move to Jackson 3 by @oryan-block in #925
- Use Kotlin's documented Maven setup to compile Kotlin before Java by @oryan-block in #928
- Declare central.sonatype.com as a repository origin by @oryan-block in #929
- Clean up the build and README for 15.0.0 by @oryan-block in #930
- Keep the values of Kotlin custom scalars without visible properties in arguments by @oryan-block in #931
- Fail on custom scalars without visible properties written with a type or object id by @oryan-block in #934
- Link the How graphql-java-tools builds a schema wiki page from the README by @oryan-block in #936
- Unwrap a DataFetcherResult inside an Optional by @oryan-block in #940
- Wait on a future inside a generic wrapper whose transformer returns a future by @oryan-block in #941
- Update all non-major dependencies by @renovate[bot] in #935
Full Changelog: v14.5.0...v15.0.0