Releases: graphql-java/graphql-java
23.1
23.0
Notice: there is a bug with https://github.com/graphql-java/graphql-java/pull/3871/files, a new bugfix release is about to be released. Use 23.1 instead, not 23.0
Thanks to everyone for contributing to this release, through pull requests, issues, and discussions!
This is a major release which contains breaking changes.
Key changes
-
Enable batching on Mutations #3737
-
Support for draft error handling directive
@experimental_disableErrorPropagation
#3772 -
DataFetchers can now return reactive-streams Publisher objects from them and they will be turned into a CompleteableFuture value. Note only a single value will be retrieved from the Publisher #3731
-
We changed from JetBrains nullability annotations to JSpecify annotations #3851. JSpecify is a new industry consortium effort, which has been adopted by Spring. See more in the documentation: https://jspecify.dev/docs/start-here/
-
A new experimental callback is available to track the execution state of GraphQL Java: it allows for being notified when GraphQL Java is running actively code vs waiting/finished. See https://github.com/graphql-java/graphql-java/blob/master/src/test/groovy/graphql/EngineRunningTest.groovy#L174 for how to use it
-
We have also released DataLoader 4.0.0 and made graphql-java dependent on it. See the release notes: https://github.com/graphql-java/java-dataloader/releases
-
@defer
directive is now included in the schema by default #3839
Performance improvements
-
Only call overlapping validation once during validation, which will significantly reduce validation time, with more savings for larger operations. We found ~150% improvement in benchmarks. #3835
-
Reducing object allocations and overhead from stream, thanks @kilink for contributing many improvements #3804 #3803 #3801 #3798 #3797 #3710
-
Reducing object allocations for
PropertyDataFetcher
andSchemaGeneratorHelper
#3755 #3754 -
Improvement on
DataFetchingSelectionSet.getImmediateFields()
, if descendant traversal is not required this saves considerable time. Thanks @timward60 #3855 -
Comparing performance improvements. We run performance benchmarks on all changes, and you can view these results inside the GitHub repo. You can then use this JMH visualisation tool to compare benchmarks from different commits, for example these before and after results from the DataFetcherSelectionSet optimisation: https://jmh.morethan.io/?sources=https://raw.githubusercontent.com/graphql-java/graphql-java/refs/heads/master/performance-results/2025-03-18T23%3A42%3A07Z-3d7dce5e49dfbe92d656629ae4fdbab979bba8be-jdk17.json,https://raw.githubusercontent.com/graphql-java/graphql-java/refs/heads/master/performance-results/2025-03-20T04%3A24%3A26Z-9a931ba6ad8e2ee49ea48c98ca86c2901f2343b1-jdk17.json
See all performance improvements on GitHub: https://github.com/graphql-java/graphql-java/issues?q=is%3Amerged%20label%3A%22performance%22%20milestone%3A%2223.0%20breaking%20changes%22%20
Breaking changes
-
Strict runtime wiring redefinition checks now on by default. We now have stricter runtime wiring redefinition checks by default to catch invalid cases. For example, a field that already had a DataFetcher registered must not have a second DataFetcher registered. This can be toggled off. #3824
-
Removed an unused protected method in ExecutionStrategy. #3881
-
A small breaking change to
AstPrinter
to enable re-use of StringBuilders #3853 -
Improvement in a few directive and applied directive builders to address a bug where all elements were cleared. #3825
-
Breaking change for a new specification requirement, to prevent
@include
and@skip
directives on subscription root fields #3871
See all breaking changes on GitHub: https://github.com/graphql-java/graphql-java/issues?q=is%3Amerged%20label%3A%22breaking%20change%22%20milestone%3A%2223.0%20breaking%20changes%22
Security
We have become a CVE Numbering Authority (CNA) for GraphQL Java and related projects. For more information, see our Security page https://www.graphql-java.com/security.
What's Changed
- Bump net.bytebuddy:byte-buddy from 1.15.1 to 1.15.5 by @dependabot in #3726
- Bump org.jetbrains:annotations from 24.1.0 to 26.0.1 by @dependabot in #3722
- Bump org.junit.jupiter:junit-jupiter from 5.11.0 to 5.11.3 by @dependabot in #3727
- Bump io.projectreactor:reactor-core from 3.6.9 to 3.6.11 by @dependabot in #3728
- Removed empty file ExecuteObjectInstrumentationContextAdapter by @bbakerman in #3732
- Bump net.bytebuddy:byte-buddy-agent from 1.15.1 to 1.15.7 by @dependabot in #3734
- Bump net.bytebuddy:byte-buddy from 1.15.5 to 1.15.7 by @dependabot in #3735
- Bump com.fasterxml.jackson.core:jackson-databind from 2.17.2 to 2.18.0 by @dependabot in #3733
- Bump google-github-actions/auth from 2.1.5 to 2.1.6 by @dependabot in #3718
- Bump net.bytebuddy:byte-buddy from 1.15.7 to 1.15.10 by @dependabot in #3743
- Bump com.fasterxml.jackson.core:jackson-databind from 2.18.0 to 2.18.1 by @dependabot in #3742
- Bump net.bytebuddy:byte-buddy-agent from 1.15.7 to 1.15.10 by @dependabot in #3741
- Bump google-github-actions/auth from 2.1.6 to 2.1.7 by @dependabot in #3740
- Bump org.eclipse.jetty:jetty-server from 11.0.22 to 11.0.24 by @dependabot in #3705
- Avoid unnecessary String allocations in AstPrinter by @kilink in #3710
- Added some more tests around providing default values by @bbakerman in #3709
- Remove SDLExtensionDefinition from InterfaceTypeDefinition by @gnawf in #3739
- Bump org.codehaus.groovy:groovy from 3.0.22 to 3.0.23 by @dependabot in #3745
- initial tests for regular performance testing by @andimarek in #3746
- Implement equals/hashCode for GraphqlErrorImpl by @AlexandreCarlton in #3720
- Bump io.projectreactor:reactor-core from 3.6.11 to 3.7.0 by @dependabot in #3748
- Add test result comment bot by @dondonz in #3749
- SchemaGeneratorHelper that does NOT put in an entry for defaulted values by @bbakerman in #3755
- Singleton property data fetcher by @bbakerman in #3754
- Filter out negative line and column error locations in toSpecification by @dondonz in #3753
- Enable batching on Mutations by @bbakerman in #3737
- Added support for reactive Publishers to be returned from data fetchers by @bbakerman in #3731
- Bump biz.aQute.bnd.builder from 6.4.0 to 7.1.0 by @dependabot in #3762
- Revert "Bump biz.aQute.bnd.builder from 6.4.0 to 7.1.0" by @bbakerman in #3763
- Bump com.fasterxml.jackson.core:jackson-databind from 2.18.1 to 2.18.2 by @dependabot in #3761
- Update SchemaDirectiveWiringEnvironment.java by @rafaeldcfarias in #3764
- ResultPath: Check object type before casting to int by @alex-cova in #3765
- Make deprecated reason non-null by @dondonz in #3759
- Deprecation time by @dondonz in #3768
- Security updates by @dondonz in #3767
- Adding hashcode and equals for defer & stream payloads by @dondonz in #3769
- Now there are zero javadoc errors during compile by @bbakerman in #3770
- Bump io.projectreactor:reactor-core from 3.7.0 to 3.7.1 by @dependabot in #3776
- Bump org.junit.jup...
22.3
This is a bugfix release focusing on the @defer
directive implementation. There are no breaking changes in this release.
Thanks to the community for continuing to report issues and build improvements, which helps make graphql-java better! Kudos to @felipe-gdr and @jbellenger for contributing @defer
improvements in this release.
Key Changes
What's Changed
- Bump google-github-actions/auth from 2.1.3 to 2.1.4 by @dependabot in #3684
- transformable IncrementalExecutionResult by @jbellenger in #3693
- Defer dataloader integration by @felipe-gdr in #3639
- Bump net.bytebuddy:byte-buddy from 1.14.18 to 1.15.1 by @dependabot in #3699
- Bump google-github-actions/auth from 2.1.4 to 2.1.5 by @dependabot in #3695
- Bump net.bytebuddy:byte-buddy-agent from 1.14.18 to 1.15.1 by @dependabot in #3700
- Bump io.projectreactor:reactor-core from 3.6.8 to 3.6.9 by @dependabot in #3689
- Bump org.junit.jupiter:junit-jupiter from 5.10.3 to 5.11.0 by @dependabot in #3688
Full Changelog: v22.2...v22.3
22.2
This is a bugfix release. There are no breaking changes in this release.
Thanks to everyone in the community for reporting issues and contributing bugfixes for this release! And happy birthday to GraphQL Java, who recently turned 9!
Key changes:
- Bugfix for DataLoader dispatching which caused subscriptions to hang #3673. First reported to Spring for GraphQL spring-projects/spring-graphql#1019
- Improved
@oneOf
validation to run sooner in the validation phase #3577 #3580 - MultiSourceReader bugfix to handle different new line behaviour in Java 16+, thanks @gummybug #3670
What's Changed
- Bump net.bytebuddy:byte-buddy from 1.14.15 to 1.14.16 by @dependabot in #3606
- Bump net.bytebuddy:byte-buddy-agent from 1.14.15 to 1.14.16 by @dependabot in #3604
- Bump org.assertj:assertj-core from 3.25.3 to 3.26.0 by @dependabot in #3605
- Better javadoc on beginFieldFetching by @bbakerman in #3610
- nested nullable list validation bugfix by @jbellenger in #3599
- Adding missed properties on IncrementalExecutionResultImpl.Builder.from by @Juliano-Prado in #3593
- This provides validation on @OneOf input types during validation phase by @bbakerman in #3577
- Bump net.bytebuddy:byte-buddy from 1.14.16 to 1.14.17 by @dependabot in #3612
- Bump net.bytebuddy:byte-buddy-agent from 1.14.16 to 1.14.17 by @dependabot in #3613
- This will validate @OneOf variable values when the variables are coerced by @bbakerman in #3580
- Fix stale bot cache problem by @dondonz in #3615
- Add option to redact token details from parser error messages by @dondonz in #3618
- Make FetchedValue a public constructor by @bbakerman in #3624
- AstPrinter: Empty types should not include braces
{}
by @tinnou in #3619 - Add toString() implementations for RawVariables and CoercedVariables classes by @oliverlockwood in #3629
- Small tweak allowed on Java 9 and later by @bbakerman in #3635
- Subscription results keep in upstream order by @bbakerman in #3574
- Start draining deferred results on subscription by @bbakerman in #3634
- Fix builder return type and expose generic toSpecification by @gnawf in #3642
- Bump io.projectreactor:reactor-core from 3.6.5 to 3.6.7 by @dependabot in #3643
- Only use JetBrains nullability annotations by @dondonz in #3644
- Bump org.junit.jupiter:junit-jupiter from 5.10.2 to 5.10.3 by @dependabot in #3653
- Bump org.codehaus.groovy:groovy from 3.0.21 to 3.0.22 by @dependabot in #3654
- This introduces a Traversal Options to allow skipping the coercing of field arguments by @bbakerman in #3651
- produce better exception for diffing bug by @andimarek in #3655
- Add missing NamedNode interface to OperationDefinition by @dondonz in #3646
- Fix bug with error handling in the defer execution code by @felipe-gdr in #3640
- Bump org.eclipse.jetty:jetty-server from 11.0.21 to 11.0.22 by @dependabot in #3659
- Bump com.fasterxml.jackson.core:jackson-databind from 2.17.1 to 2.17.2 by @dependabot in #3660
- Bump io.projectreactor:reactor-core from 3.6.7 to 3.6.8 by @dependabot in #3666
- Bump net.bytebuddy:byte-buddy from 1.14.17 to 1.14.18 by @dependabot in #3664
- Bump org.assertj:assertj-core from 3.26.0 to 3.26.3 by @dependabot in #3665
- Bump net.bytebuddy:byte-buddy-agent from 1.14.17 to 1.14.18 by @dependabot in #3667
- Fix MultiSourceReader compatibility with Java 16+ by @gummybug in #3670
- Use static function to initialize a flag in MultiSourceReader by @gummybug in #3671
- upgrade gradle to 8.9 by @andimarek in #3672
- 3662 - fixes dataloader dispatching during subscriptions by @bbakerman in #3673
- upgrade gradle wrapper by @andimarek in #3675
- Add missing directive definitions by @dondonz in #3656
- schema diffing improvements by @andimarek in #3676
New Contributors
- @oliverlockwood made their first contribution in #3629
- @gummybug made their first contribution in #3670
Full Changelog: v22.1...v22.2
22.1
This is a bugfix release. There are no breaking changes in this release, however if you use Kotlin, please note some nullability changes were made with this release.
Key changes include:
- Fix to add field fetching method #3571
- Add validation rule for
@deprecated
on required arguments and input object fields #3591 - Nullability changes for DataFetchingEnvironment #3582. If you use Kotlin, this may be considered a breaking change for you. See discussion: #3630
Thanks to everyone who contributed to this release!
What's Changed
- Update readme badge by @dondonz in #3568
- Added a getBoolean onto GraphQLContext.java by @bbakerman in #3578
- getBoolean returns a boolean not a Boolean by @bbakerman in #3581
- Bump org.testng:testng from 7.10.1 to 7.10.2 by @dependabot in #3583
- Bump net.bytebuddy:byte-buddy from 1.14.13 to 1.14.14 by @dependabot in #3584
- Bump net.bytebuddy:byte-buddy-agent from 1.14.13 to 1.14.14 by @dependabot in #3585
- Bump com.fasterxml.jackson.core:jackson-databind from 2.17.0 to 2.17.1 by @dependabot in #3587
- Always include incremental props even if null by @gnawf in #3570
- Bump net.bytebuddy:byte-buddy-agent from 1.14.14 to 1.14.15 by @dependabot in #3595
- Bump net.bytebuddy:byte-buddy from 1.14.14 to 1.14.15 by @dependabot in #3596
- Ready: Add @deprecated validation for input object fields, field arguments, directive arguments by @dondonz in #3591
- Added field fetching method for Expedia by @bbakerman in #3571
- Update GitHub actions ahead of release by @dondonz in #3600
- Bump google-github-actions/auth from 2.1.2 to 2.1.3 by @dependabot in #3603
- Bump org.eclipse.jetty:jetty-server from 11.0.20 to 11.0.21 by @dependabot in #3602
- Bump com.google.code.gson:gson from 2.10.1 to 2.11.0 by @dependabot in #3601
- Add nullability annotations in DataFetchingEnvironment & sub-classes by @Salzian in #3582
- Now does not overwrite a types default data fetcher unless its nonnull by @bbakerman in #3579
- Add extend schema (AST print) to SchemaPrinter by @dondonz in #3471
New Contributors
Full Changelog: v22.0...v22.1
22.0
We are pleased to announce the release of graphql-java v22.0.
Thanks to everyone in the community who contributed to the release, whether that was code, helping to report issues, or participating in discussions.
This is a breaking change release.
The graphql-java team takes breaking changes very seriously but in the name of performance we have made some significant breaking changes in this release.
Major Performance Changes
Past releases have been very much performance focused and this one is no different. However, this release is aiming to reduce memory pressure more than reduce pure CPU usage. When the graphql-java engine is running, if it produces less objects it will ultimately run faster because of reduced memory pressure and less garbage to collect.
The engine has been changed in two major ways that reduce memory pressure.
ExecutionResult wrapping
The first was that all values that come back got wrapped internally into a ExecutionResult
object where the data
attribute was the value. This was a carry over from some very early GraphQL code but was unnecessary and hence has been removed. The follow on effect of this is that some graphql.execution.ExecutionStrategy
protected methods and graphql.execution.instrumentation.Instrumentation
methods used to receive and return ExecutionResult
values and no longer do, which is an API breaking change. We have made this breaking changes in the name of memory pressure performance.
CompletableFuture wrapping
The second major change is that the engine no longer exclusively uses java.util.concurrent.CompletableFuture
s when composing together results. Let us explain the past code first so we can discuss the new code.
CompletableFuture
is a fantastic construct because it represents a promise to a value and it can also hold an exceptional state inside itself. Async programming with CompletableFuture
is viral. If stage 1 of some compute process returns a CompletableFuture
then stage 2 and 3 and 4 are likely to as well to ensure everything is asynchronous.
We use to take values that were not asynchronous (such as DataFetcher that returned a simple in memory object) and wrap them in a CompletableFuture
so that all of the other code composed together using CompletableFuture
methods like .thenApply()
and .thenCompose
and so on. The code is cleaner if you use all CompletableFuture
code patterns.
However many objects in a GraphQL request are not asynchronous but rather materialised objects in memory. Scalars, enums and list are often just values immediately available to be used and allocating a CompletableFuture
makes the code easier to write but it has a memory pressure cost.
So we have sacrificed cleaner code for more memory performant code.
graphql-java engine graphql.execution.ExecutionStrategy
methods now just return Object
where that object might be either a CompletableFuture
or a materialised value.
private Object /*CompletableFuture<FetchedValue> | FetchedValue>*/
fetchField(GraphQLFieldDefinition fieldDef, ExecutionContext executionContext, ExecutionStrategyParameters parameters) {
Notice we have lost type safety here. In the past this would have only been CompletableFuture<FetchedValue>
and hence been both type safe and async composable.
Now the caller of that method has to handle the async case where it might be a CompletableFuture<FetchedValue>
AND the materialised value case where it might be a FetchedValue
but as most simple fields in GraphQL are in fact materialised values, this is worth the less clean code.
DataFetchers
can of course continue to return CompletableFuture
values and they will be handled in a polymorphic manner.
The upshot of all this work is that the graphql-java engine allocated way less CompletableFuture
values and hence reduces the amount of memory used.
Instrumentation Changes
The above changes now mean means that before graphql.execution.instrumentation.InstrumentationContext
used to be given a CompletableFuture
but now no longer does
void onDispatched(CompletableFuture<T> result);
is now
void onDispatched();
if you previously used the CompletableFuture
to know when something was completed, well InstrumentationContext
has the same semantics because it's completed method is similar in that it presents a value or an exception
void onCompleted(T result, Throwable t);
graphql.execution.instrumentation.Instrumentation
also had a lot of deprecated methods in place and these have been removed since the more performant shape of passing in graphql.execution.instrumentation.InstrumentationState
has been in place for quite a few releases.
Some of the methods that received ExecutionResult
wrapped values have also changed as mentioned above.
Instrumentation
is probably the area that needs the most attention in terms of breaking changes. If you have not moved off the deprecated methods, your custom Instrumentation
s will no longer compile or run.
Interning key strings
Our friends at Netflix provided a PR that interns certain key strings like "field names" so that we create less String instances when processing field names in queries that we know must be previously interned in the schema.
The full list of performance related changes
Major Changes
@defer Experimental Support
Support for the @defer
directive has been added in an experimental fashion. While @defer
is still not in the released GraphQL specification, we have judged it mature enough to support at this stage and the graphql-js
reference implementation has support for it.
https://github.com/graphql/graphql-wg/blob/main/rfcs/DeferStream.md
At this stage we have not yet supported @stream
but this is likely to be included in a future release.
Breaking Changes
There are quite a few API breaking changes in this release. In fact there are 22 PRs containing breaking API changes.
Stricter parseValue coercion: Aligning with JS reference implementation
We have made changes to String, Boolean, Float, and Int parseValue
coercion, to be consistent with the reference JS implementation. The key change is parseValue
is now stricter on accepted inputs.
- String
parseValue
now requires input of typeString
. For example, aNumber
input123
or aBoolean
inputtrue
will no longer be accepted. - Boolean
parseValue
now requires input of typeBoolean
. For example, aString
input"true"
will no longer be accepted. - Float
parseValue
now requires input of typeNumber
. For example, aString
input"3.14"
will no longer be accepted. - Int
parseValue
now requires input of typeNumber
. For example, aString
input"42"
will no longer be accepted.
This is a breaking change. To help you migrate, in version 21.0, we introduced the InputInterceptor #3188 (and an implementation LegacyCoercingInputInterceptor #3218) to provide a migration pathway. You can use this interceptor to monitor and modify values.
Please take a look at our documentation page for more information and examples: https://www.graphql-java.com/documentation/upgrade-notes
For more, see #3553
JS reference implementation: https://github.com/graphql/graphql-js/blob/main/src/type/scalars.ts
Removing deprecated methods
Many methods that have been deprecated for a long time, sometimes even up to 6 years, have finally been removed.
The CompletableFuture
unwrapping work mentioned above changed many methods in the graphql.execution.ExecutionStrategy
classes but we don't expect this affect many people since very few people write their own engines.
That CompletableFuture
unwrapping work also had knock on effects as mentioned above on graphql.execution.instrumentation.Instrumentation
and hence this is the most likely place for there to be compatibility challenges in existing code.
DataLoaderDispatcherInstrumentation has been removed and is now built into the engine
The code to dispatch org.dataloader.DataLoader
s used to be an instrumentation called DataLoadersDataLoaderDispatcherInstrumentation
and it was automatically added at runtime. This approach has been removed.
The equivalent code is now built into the graphql-java engine itself. DataLoader
s can now always be used without any special setup. This also allows the code to be more performant in how DataLoader
s are dispatched.
SL4J logging has been removed
Previously, the graphql-java engine would log at DEBUG level certain errors or when fields get fetched. This has not proven used for from a support point to the graphql-java team and also has a compliance cost in that user generated content (UGC) and personally identifying information (PII) could end up being logged, which may not be allowed under regimes like European General Data Protection Regulation (GDPR).
If you want to log now we suggest you invest in a Instrumentation
that logs key events and you can there for log what you want and in a compliant manner of your choosing.
The full list of API breaking changes
Other changes
- Optional strict mode for RuntimeWiring and TypeRuntimeWiring, to avoid accidentally having multiple datafetchers on the same element #3565
...
21.5
20.9
19.11
21.4
This is a special release to help control introspection queries.
This release adds a default check for introspection queries, to check that they are sensible. This feature is a backport of #3526 and #3527.
This release also adds an optional maximum result nodes limit, which is a backport of #3525.
What's Changed
- 21.x Backport PR 3526 and PR 3527 by @dondonz in #3529
- 21.x backport 3525 max result nodes by @dondonz in #3528
Full Changelog: v21.3...v21.4