This release contains a bunch of fixes and minor improvements.
Many thanks to @adubovkin and @ndwhelan for contributing to the project, and to all the people who sent feedback! 💜
@JsName annotation to Operation.name() (#4643)@include or @skip with default values (#4700)As we're starting to work on version 4.0 which will drop support for the "compat" codegen and a few other options dating from version 2, we've added in this release some deprecation warnings that will warn when they're used. If you haven't done already, now is a good time to migrate!
@skip and @include (#4645)This release contains a handful of bug fixes and improvements.
This release contains a handful of bug fixes and improvements, and also discontinues the legacy JS artifacts.
Many thanks to @StefanChmielewski and @chao2zhang for contributing to the project! 🧡
Historically, Kotlin Multiplatform has had 2 formats of JS artifacts: Legacy and IR, and Apollo Kotlin has been publishing both. However, the Legacy format is about to be deprecated with Kotlin 1.8 and moreover we've seen issues when using the Legacy artifact in the browser. That is why starting with this release, only the IR artifacts will be published. Please reach out if this causes any issue in your project.
GraphQLWsProtocol.Factory.webSocketPayloadComposer (#4589)alwaysGenerateTypesMatching to execution time (#4578)service {} in all messages/docs (#4572)This patch release brings a few fixes.
Many thanks to @davidshepherd7, @chao2zhang, @agrosner, @MyDogTom, @doucheng, @sam43 and @vincentjames501, for helping improve the library! 🙏
Apollo Kotlin can be configured to work with multiple services and have the package name, schema files location, and other options specified for each of them. When using a single service however it is possible to omit the service block and set the options directly in the apollo block - in that case, a default service named service is automatically defined.
While this saves a few lines, it relies on Gradle afterEvaluate {} block that makes the execution of the plugin less predictable and more subject to race conditions with other plugins (see here for an example).
What's more, as we move more logic to build time, the name of the service is going to be used more and more in generated code. Since explicit is better than implicit, mandating that service name sounds a good thing to do and a warning is now printed if you do not define your service name.
To remove the warning, embed the options into a service block:
apollo {
+ service("service") {
packageName.set("com.example")
// ...
+ }
}
canBeBatched and httpHeaders orthogonal (#4534)A patch release with a few fixes.
Many thanks to @Holoceo, @juliagarrigos, @davidshepherd7 and @eduardb for the feedbacks 💙
A patch release to fix an issue where the ApolloCall could end up in a bad state. Many thanks to @WilliamsDHI for diving into this 💙!
This version adds multiple new low level features. These new features expose a lot of API surface, and they will probably stay experimental until 4.0. Feedback is always very welcome.
Compiler hooks allow you to tweak the generated models by exposing the underlying JavaPoet/KotlinPoet structures. You can use it for an example to:
__typename (source)To do so, make sure to use the "external" version of the plugin:
plugins {
// Note: using the external plugin here to be able to reference KotlinPoet classes
id("com.apollographql.apollo3.external")
}
And then register your hook to the plugin:
apollo {
service("defaultnullvalues") {
packageName.set("hooks.defaultnullvalues")
compilerKotlinHooks.set(listOf(DefaultNullValuesHooks()))
}
}
By default, Apollo Kotlin models fragments with synthetic nullable fields. If you have a lot of fragments, checking these fields requires using if statements. For an example, with a query like so:
{
animal {
species
... on WarmBlooded {
temperature
}
... on Pet {
name
}
... on Cat {
mustaches
}
}
}
you can access data like so:
if (animal.onWarmBlooded != null) {
// Cannot smart cast because of https://youtrack.jetbrains.com/issue/KT-8819/
println(animal.onWarmBlooded!!.temperature)
}
if (animal.onPet != null) {
println(animal.onPet!!.name)
}
if (animal.onCat != null) {
println(animal.onCat!!.mustaches)
}
Some of the combinations could be impossible. Maybe all the pets in your schema are warm blooded. Or maybe only cat is a warm blooded. To model this better and work around KT-8819, @chalermpong implemented a new codegen that adds a base sealed interface. Different implementations contain the same synthetic fragment fields as in the default codegen except that their nullability will be updated depending the branch:
when (animal) {
is WarmBloodedPetAnimal -> {
println(animal.onWarmBlooded!!.temperature)
println(animal.onPet!!.name)
}
is PetAnimal -> {
// Some pet that is not warm blooded, e.g. a Turtle maybe?
println(animal.onPet!!.name)
}
is OtherAnimal -> {
println(animal.species)
}
// Note how there is no branch for Cat because it's a WarmBloodedPetAnimal
// Also no branch for WarmBlooded animal because all pets in this (fictional) sample schema are WarmBlooded. This could be different in another schema
}
To try it out, add this to your Gradle scripts:
apollo {
codegenModels.set("experimental_operationBasedWithInterfaces")
}
Many many thanks to @chalermpong for diving into this 💙
By default, Apollo Kotlin only generates the types that are used in your queries. This is important because some schemas are really big and generating all the types would waste a lot of CPU cycles. In multi-modules scenarios, the codegen only knows about types that are used locally in that module. If two sibling modules use the same type and that type is not used upstream, that could lead to errors like this:
duplicate Type '$Foo' generated in modules: feature1, feature2
Use 'alwaysGenerateTypesMatching' in a parent module to generate the type only once
This version introduces new options to detect the used types automatically. It does so by doing a first pass at the GraphQL queries to determine the used type. Upstream modules can use the results of that computation without creating a circular dependency. To set up auto detection of used coordinates, configure your schema module to get the used coordinates from the feature module using the apolloUsedCoordinates configuration:
// schema/build.gradle.kts
dependencies {
implementation("com.apollographql.apollo3:apollo-runtime")
// Get the used coordinates from your feature module
apolloUsedCoordinates(project(":feature"))
// If you have several, add several dependencies
apolloUsedCoordinates(project(":feature-2"))
}
apollo {
service("my-api") {
packageName.set("com.example.schema")
generateApolloMetadata.set(true)
}
}
And in each of your feature module, configure the apolloSchema dependency:
// feature/build.gradle.kts
dependencies {
implementation("com.apollographql.apollo3:apollo-runtime")
// Depend on the codegen from the schema
apolloMetadata(project(":schema"))
// But also from the schema so as not to create a circular dependency
apolloSchema(project(":schema"))
}
apollo {
// The service names must match
service("my-api") {
packageName.set("com.example.feature")
}
}
registerJavaGeneratingTask, fixes lint trying to scan generated sources (#4486)generateModelBuilder to generateModelBuilders and add test (#4476)JSWebSocketEngine (#4445)A patch version to fix compatibility with Kotlin 1.7.20 and another fix when calling ApolloStore.dump() with the SQL normalized cache.
A patch version to fix an issue with data builder and multi modules. Many thanks to @agrosner and @eduardb for catching this.
This version brings initial support for @defer as well as data builders.
Many thanks to @engdorm, @Goooler, @pt2121 and @StylianosGakis for their contributions!
@defer support@defer support is experimental in the Kotlin Client and currently a Stage 2 GraphQL specification draft to allow incremental delivery of response payloads.
@defer allows you to specify a fragment as deferrable, meaning it can be omitted in the initial response and delivered as a subsequent payload. This improves latency for all fields that are not in that fragment. You can read more about @defer in the RFC and contribute/ask question in the @defer working group.
Apollo Kotlin supports @defer by default and will deliver the successive payloads as Flow items. Given the below query:
query GetComputer {
computer {
__typename
id
...ComputerFields @defer
}
}
fragment ComputerFields on Computer {
cpu
year
screen {
resolution
}
}
And the following server payloads:
payload 1:
{
"data": {
"computer": {
"__typename": "Computer",
"id": "Computer1"
}
},
"hasNext": true
}
payload 2:
{
"incremental": [
{
"data": {
"cpu": "386",
"year": 1993,
"screen": {
"resolution": "640x480"
}
},
"path": [
"computer",
]
}
],
"hasNext": true
}
You can listen to payloads by using toFlow():
apolloClient.query(query).toFlow().collectIndexed { index, response ->
// This will be called twice
if (index == 0) {
// First time without the fragment
assertNull(response.data?.computer?.computerFields)
} else if (index == 1) {
// Second time with the fragment
assertNotNull(response.data?.computer?.computerFields)
}
}
You can read more about it in the documentation.
As always, feedback is very welcome. Let us know what you think of the feature by either opening an issue on our GitHub repo , joining the community or stopping by our channel in the KotlinLang Slack(get your invite here).
Apollo Kotlin 3.0 introduced test builders. While they are working, they have several limitations. The main one was that being response based, they could generate a lot of code. Also, they required passing custom scalars using their Json encoding, which is cumbersome.
The data builders are a simpler version of the test builders that generate builders based on schema types. This means most of the generated code is shared between all your implementations except for a top level Data {} function in each of your operation:
// Replace
val data = GetHeroQuery.Data {
hero = humanHero {
name = "Luke"
}
}
// With
val data = GetHeroQuery.Data {
hero = buildHuman {
name = "Luke"
}
}
Starting with this release, Apollo Kotlin is built with Kotlin 1.7.10. This doesn't impact Android and JVM projects (the minimum supported version of Kotlin continues to be 1.5) but if you are on a project using Native, you will need to update the Kotlin version to 1.7.0+.
file.source().buffer() (#4326)A 2.x maintainance release with a couple of bugfixes. Many thanks to @eduardb for diving into #2818 💙
Full Changelog: https://github.com/apollographql/apollo-kotlin/compare/v2.5.12...v2.5.13
With this release, Apollo Kotlin now uses Kotlin Native's new memory model. It also contains a number of other improvements and bug fixes.
Many thanks to @glureau for carefully adding new watch targets ⌚💙
Apollo Kotlin is now requiring applications to use the new memory manager, a.k.a. new memory model. Thanks to this change, the restriction that operations had to be executed from the main thread on Apple targets is now removed. You can also use kotlinx.coroutines.test.runTest. Last but not least, benchmarks seem to indicate that performance is better under the new memory manager!
@targetName directive (#4243)This directive was introduced in v3.3.1 to allow overriding the name of enum values in the generated code. It has now been extended to allow configuring the generated name of Interfaces, Enums, Unions, Scalars and Input objects. This can be used to make the generated code nicer to use, or to avoid name clashes with Kotlin types (e.g. Long) in Kotlin Native.
From now on, you no longer need to specify explicitly the versions of Apollo dependencies: if omitted, the same version as the Apollo Gradle plugin will be used. This should facilitate upgrades and avoid potential mistakes:
plugins {
plugins {
id("org.jetbrains.kotlin.jvm").version("1.7.10")
id("com.apollographql.apollo3").version("3.5.0")
}
dependencies {
// Replace this
// implementation("com.apollographql.apollo3:apollo-runtime:3.5.0")
// with
implementation("com.apollographql.apollo3:apollo-runtime")
}
}
runTest (#4292)With the new memory model, Apollo's specific runTest method from apollo-testing-support is no longer useful and has been deprecated. If you were using it, you should now be able to use Kotlin's runTest instead, or simply runBlocking.
type enum values.If you have an enum with a type value, this value used to name clash with the generated type property. This version now detects this case automatically and escapes type to type_. If you had previously used @targetName to workaround this issue, you can now remove it to avoid it being escaped twice:
# Remove this
extend enum SomeEnum {
type @targetName(name: "type_")
}
type in enum values (#4295)2022-07-11
This release contains a few important bug fixes (#4214, #4224, #4247, #4256) and makes it possible to compile with Gradle 7.4 and apollo-gradle-plugin (#4218).
It also introduces incubating cache artifacts.
Many thanks to @ArjanSM, @zebehringer, @mm-kk-experiments, @mune0903, @stengvac, @elenigen, @shamsidinb and @StylianosGakis for the awesome contributions 😃!
incubating cache artifacts.This version introduces the below artifacts:
apollo-normalized-cache-incubatingapollo-normalized-cache-api-incubatingapollo-normalized-cache-sqlite-incubatingThese artifacts introduce new APIs to work with cache expiration and pagination (as well as other cache improvements in the future).
These artifacts have no backward compatibility guarantees and most likely have worse performance than the non-incubating artifacts. Documentation will be added once the API stabilize. In the short term, the best place to look for examples are the integration tests:
Note: The experimental withDates: Boolean argument was introduced in 3.3.1 in the regular artifacts and is removed as part of this release. Use the incubating artifacts to use it.
This is a hot fix release that fixes a crash that could happen in the codegen when using responseBased codegen in a
multimodule setup. It also includes a fix for incorrect generated code when using certain reserved names in enum values.
1.12.0, fixes generating enum values whose name clashes with other symbols (#4034)Version 2.5.12 is a maintenance release with a fix to restore downloading schemas as SDL, and a fix for Kotlin 1.7 compatibility.
💜 Many thanks to @eg-ndobrijevic and @remcomokveld for raising these issues! 💜
This release introduces @typePolicy on interface/enums, improvements on subscription error handling, and on Test Builders. It also contains a number of other improvements and bug fixes!
@typePolicy on interfaces and unions (#4131)The @typePolicy directive can now be declared on interfaces and unions. Thank you @bubba for the contribution!
An issue where websocketReopenWhen was not called in some cases was fixed. Also, this release introduces SubscriptionOperationException. A SubscriptionOperationException will be thrown instead of the more generic ApolloNetworkError if a subscription fails due to a specific operation error.
__typename is missing was made clearer (#4146)rawValue instead of name for enums (#4121)ApolloClient now implements okio.Closable so you can use use with it. Thanks @yogurtearl for this contribution!
@targetName directive on enum values (#4144)If an enum value name is clashing with a reserved name (e.g. type) you can now use this directive to instruct the codeGen to use the specified name for the value instead. This directive is experimental for now.
As we add more client directives, the risk of nameclash with existing schema directives increases. If this happens, you can now import Apollo client directives using @link:
# extra.graphqls
extend schema @link(url: "https://specs.apollo.dev/kotlin_labs/v0.1")
This adds a @kotlin_labs__ prefix to all Apollo client directives:
{
hero {
name @kotlin_labs__nonnull
}
}
SqlNormalizedCacheFactory initialization on Android (#4104)It is no longer necessary to pass a Context when initializing the SqlNormalizedCacheFactory on Android. A Context is automatically provided, via App Startup.
// Before
val sqlNormalizedCacheFactory = SqlNormalizedCacheFactory(context, "apollo.db")
// After
val sqlNormalizedCacheFactory = SqlNormalizedCacheFactory("apollo.db")
This release starts tracking the public API of all modules, including MockServer. Even if the API remains experimental, we'll try to keep the number of breaking changes low in the future.
apollo-gradle-plugin-external (#4078)Many thanks to @tajchert, @asimonigh, @hrach, @ArjanSM, @yshrsmz, @ephemient, @bubba, @eboudrant and @yogurtearl for contributing to this release! 🙏
This is the first release with HMPP support. If you're using multiplatform, updating to Kotlin 1.6.21 is strongly encouraged.
This release also brings WebSocket related improvements and other fixes!
When using Apollo Kotlin on a multiplatform project, this release is compatible with the hierarchical project structure, which makes it easier to share common code among several targets. Using HMPP in your project also fixes some issues when compiling Kotlin metadata. See https://github.com/apollographql/apollo-kotlin/issues/4019 and https://youtrack.jetbrains.com/issue/KT-51970/ for more details.
✋ Note: If you're using multiplatform, we strongly encourage updating to Kotlin 1.6.21. If that is not an option, you might have issues resolving dependencies. More infos in this issue.
WebSocketNetworkTransport.closeConnection (#4049)This new method can be used in conjunction with reopenWhen to force a reconnection to the server. This could be useful for instance when needing to pass new auth tokens in the headers. If you were using subscriptionManager.reconnect() in 2.x, closeConnection is a simple way to achieve the same behaviour.
GraphQLWsProtocol.connectionPayload is now a lambda (#4043)With GraphQLWsProtocol, if you need to pass parameters to the connection payload, previously you would pass them as a static map to the builder. With this change you can now pass a lambda providing them as needed. This facilitates passing fresh auth tokens when connecting.
You can now use the --insecure flag when downloading a schema with downloadApolloSchema, to bypass the certificate check, which can be useful if a server is configured with a self-signed certificate for instance.
Many thanks to @CureleaAndrei and @kdk96 for contributing to this release! 🙏
BearerTokenInterceptor was provided as an example but is too simple for most use cases, and has therefore been deprecated
in this release. This page provides more details
about authentication.GraphQLWsProtocol has been deprecated (see above).Many thanks to @benedict-lim, @olivierg13, @konomae and @sproctor for their contributions 💙
3.2.2 is a maintenance release to fix the addJvmOverloads option added in 3.2.0 as well as other fixes. If you're using APQs, the mutations are now always send using POST. See #4006 for details and a way to override the behaviour if you need to.
This release introduces a few improvements and bug fixes.
ApolloCall<D>.emitCacheMisses(Boolean) (#3980)When observing the cache with watch, the behavior was to not emit cache misses at all, which may not desirable in certain cases. With this new option, you can now choose to emit them: in that case responses will be emitted with a null data.
This can be used like so:
apolloClient.query(query)
.fetchPolicy(FetchPolicy.CacheOnly)
.emitCacheMisses(true)
.watch()
.collect { response ->
// response.data will be null in case of cache misses
}
This is also closer to the behavior that was in place in v2. Many thanks to @mateuszkwiecinski for the insights and raising the issue!
SubscriptionWsProtocol and default to Text (#3992)When using subscriptions over WebSockets with SubscriptionWsProtocol (the default), the frames were sent in the binary format. It was reported that this was not compatible with certain servers (DGS, graphql-java-kickstart) that are expecting text frames. This is now fixed and the default is to send text frames.
⚠️ This may be a breaking change if your server expects binary frames only! If that is the case, you can use the new
frameTypeoption to configure the frame type to be sent:
client = ApolloClient.Builder()
.webSocketServerUrl("wss://...")
.wsProtocol(GraphQLWsProtocol.Factory(frameType = WsFrameType.Binary))
.build()
Many thanks to @Krillsson and @aviewfromspace1 for the insights and raising the issue!
ApolloRequest.newBuilder(operation: Operation<E>) (#3988)Many thanks to @AdamMTGreenberg and @Krillsson for the contributions! 🙏
💙 Thanks to @undermark5, @demoritas, @rkoron007, @akshay253101, @StylianosGakis, @Goooler, @jeffreydecker, @theBradfo, @anderssandven and @olivierg13 for contributing to this release.
This version adds JS WebSocket support, more options to deal with __typename amongst other features and bugfixes.
Version 3.2.0 now has WebSocket support for Javascript targets courtesy of @undermark5! This is a huge milestone and means the JS target is now even closer to its JVM and iOS counterparts.
jvm | Apple | js | linuxX64 | |
|---|---|---|---|---|
apollo-api (models) | ✅ | ✅ | ✅ | ✅ |
apollo-runtime (network, query batching, apq, ...) | ✅ | ✅ | ✅ | 🚫 |
apollo-normalized-cache | ✅ | ✅ | ✅ | 🚫 |
apollo-adapters | ✅ | ✅ | ✅ | 🚫 |
apollo-normalized-cache-sqlite | ✅ | ✅ | 🚫 | 🚫 |
apollo-http-cache | ✅ | 🚫 | 🚫 | 🚫 |
The implementation is based on the ws library on Node and the WebSocket API on the browser and inspired by Ktor.
__typename control (#3939)This version generates non-nullable fragments when it knows the fragment is always present:
{
cat {
# Because Animal is a supertype of Cat this condition will always be true
... on Animal {
species
}
}
}
In addition, it introduces a addTypename Gradle option to have better control over when to add the __typename field:
/**
* When to add __typename. One of "always", "ifFragments", "ifAbstract" or "ifPolymorphic"
*
* - "always": Add '__typename' for every compound field
*
* - "ifFragments": Add '__typename' for every selection set that contains fragments (inline or named)
* This is adding a lot more '__typename' than the other solutions and will be certainly removed in
* a future version. If you require '__typename' explicitly, you can add it to your queries.
* This causes cache misses when introducing fragments where no fragment was present before and will be certainly removed in
* a future version.
*
* - "ifAbstract": Add '__typename' for abstract fields, i.e. fields that are of union or interface type
* Note: It also adds '__typename' on fragment definitions that satisfy the same property because fragments
* could be read from the cache and we don't have a containing field in that case.
*
* - "ifPolymorphic": Add '__typename' for polymorphic fields, i.e. fields that contains a subfragment
* (inline or named) whose type condition isn't a super type of the field type.
* If a field is monomorphic, no '__typename' will be added.
* This adds the bare minimum amount of __typename but the logic is substantially more complex and
* it could cause cache misses when using fragments on monomorphic fields because __typename can be
* required in some cases.
*
* Note: It also adds '__typename' on fragment definitions that satisfy the same property because fragments
* could be read from the cache and we don't have a containing field in that case.
*
* Default value: "ifFragments"
*/
You can read more in the corresponding Typename.md design document.
The Apollo Gradle plugin now creates a new "apollo" publication if maven-publish is found. This means you can now publish the Apollo metadata to a maven repository:
# In your producer project
./gradlew publishApolloPublicationTo[SomeRepository]
Assuming your producer project is using com.example:project:version for maven coordinates, the Apollo metadata will be published at com.example:project-apollo:version:
// In your consumer project
dependencies {
implementation("com.example:project:version")
apolloMetadata("com.example:project-apollo:version")
}
Note: There are absolutely no forward/backward compatibility guarantees for Apollo metadata yet. The Apollo version used in the consumer must be the same as the one used in the producer.
addJvmOverloads Gradle option (#3907)For better Java interop, you can now opt-in addJvmOverloads. addJvmOverloads will add the @JvmOverloads to your Kotlin operations:
@JvmOverloads
class GetHeroQuery(val id: String, val episode: Optional<Episode> = Optional.Absent) {
// ...
}
Meaning you can now create a new query from Java without having to specify episode: new GetHeroQuery("1002")
graphql-ws library to tutorial (#3961) __typename IrProperty (#3930)