ย A strongly-typed, caching GraphQL client for the JVM, Android, and Kotlin multiplatform.
MIT License
Bot releases are hidden (Show)
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)Published by martinbonnin about 2 years ago
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)Published by martinbonnin over 2 years ago
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-incubating
apollo-normalized-cache-api-incubating
apollo-normalized-cache-sqlite-incubating
These 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.
Published by BoD over 2 years ago
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)Published by BoD over 2 years ago
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! ๐
Published by BoD over 2 years ago
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! ๐
Published by BoD over 2 years ago
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 deprecatedGraphQLWsProtocol
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.
Published by BoD over 2 years ago
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 newframeType
option 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)Version 3.1.0 introduces new APIs for testing, mapping scalars as well a redesigned cache pipeline.
It also contains bugfixes around the @include
directives, MemoryCache and GraphQL validation amongst other changes.
useSchemaPackageNameForFragments
(#3775)If you're using packageNameFromFilePath()
, the package name of generated fragment classes has changed.
Different generated types have different package names:
Previously, fragments were using the schema path which is inconsistent because fragments are not defined in the schema but are executable files, like operations.
Version 3.1.0 now uses the same logic for fragments as for operations. To revert to the previous behaviour, you can use useSchemaPackageNameForFragments
:
apollo {
useSchemaPackageNameForFragments.set(true)
}
This is also done automatically if you're using useVersion2Compat()
. Moving forward, the plan is to remove useSchemaPackageNameForFragments
in favor of setting a custom PackageNameGenerator
. If you have use cases that require useSchemaPackageNameForFragments
, please reach out.
QueueTestNetworkTransport
(#3757)3.1.0 introduces QueueTestNetworkTransport
to test at the GraphQL layer without needing to run an HTTP server.
To use it, configure your ApolloClient
:
// This uses a QueueTestNetworkTransport that will play the queued responses
val apolloClient = ApolloClient.Builder()
.networkTransport(QueueTestNetworkTransport())
.build()
You can then use the enqueueTestResponse
extension function to specify the GraphQL responses to return:
val testQuery = GetHeroQuery("001")
val testData = GetHeroQuery.Data {
hero = droidHero {
name = "R2D2"
}
}
apolloClient.enqueueTestResponse(testQuery, testData)
val actual = apolloClient.query(testQuery).execute().data!!
assertEquals(testData.hero.name, actual.hero.name)
MockServerHandler
(#3757)If you're testing at the HTTP layer, you can now define your own MockServerHandler
to customize how the server is going to answer to requests:
val customHandler = object : MockServerHandler {
override fun handle(request: MockRequest): MockResponse {
return if (/* Your custom logic here */) {
MockResponse(
body = """{"data": {"random": 42}}""",
headers = mapOf("X-Test" to "true"),
)
} else {
MockResponse(
body = "Internal server error",
statusCode = 500,
)
}
}
}
val mockServer = MockServer(customHandler)
FetchPolicy.CacheAndNetwork
(#3828)Previously, FetchPolicy
s were limited to policies that emitted at most one response. There was a executeCacheAndNetwork()
method but it felt asymmetrical. This version introduces FetchPolicy.CacheAndNetwork
that can emit up to two responses:
apolloClient.query(query)
// Check the cache and also use the network (1 or 2 values can be emitted)
.fetchPolicy(FetchPolicy.CacheAndNetwork)
// Execute the query and collect the responses
.toFlow().collect { response ->
// ...
}
ApolloCall<D>.fetchPolicyInterceptor(interceptor: ApolloInterceptor)
(#3743)If you need more customized ways to fetch data from the cache or more fine-grained error handling that does not come with the built-in FetchPolicy
, you can now use fetchPolicyInterceptor
:
// An, interceptor that will only use the network after getting a successful response
val refetchPolicyInterceptor = object : ApolloInterceptor {
var hasSeenValidResponse: Boolean = false
override fun <D : Operation.Data> intercept(request: ApolloRequest<D>, chain: ApolloInterceptorChain): Flow<ApolloResponse<D>> {
return if (!hasSeenValidResponse) {
CacheOnlyInterceptor.intercept(request, chain).onEach {
if (it.data != null) {
// We have valid data, we can now use the network
hasSeenValidResponse = true
}
}
} else {
// If for some reason we have a cache miss, get fresh data from the network
CacheFirstInterceptor.intercept(request, chain)
}
}
}
apolloClient.query(myQuery)
.refetchPolicyInterceptor(cacheOnlyInterceptor)
.watch()
.collect {
//
}
Service.mapScalar
Gradle API (#3779)You can now use mapScalar
to specify your scalar mappings:
apollo {
// Replace
customScalarsMapping.set(mapOf(
"Date" to "java.util.Date"
))
// With
mapScalar("Date", "java.util.Date")
}
mapScalar
also works with built-in scalar types so you can map the ID
type to a kotlin Long:
apollo {
// This requires registering an adapter at runtime with `addCustomScalarAdapter()`
mapScalar("ID", "kotlin.Long")
}
As an optimization, you can also provide the adapter at compile time. This will avoid a lookup at runtime everytime such a scalar is read:
apollo {
// No need to call `addCustomScalarAdapter()`, the generated code will use the provided adapter
mapScalar("ID", "kotlin.Long", "com.apollographql.apollo3.api.LongAdapter")
}
For convenience, a helper function is provided for common types:
apollo {
// The generated code will use `kotlin.Long` and the builtin LongAdapter
mapScalarToKotlinLong("ID")
// The generated code will use `kotlin.String` and the builtin StringAdapter
mapScalarToKotlinString("Date")
// The generated code will use `com.apollographql.apollo3.api.Upload` and the builtin UploadAdapter
mapScalarToUpload("Upload")
}
convertApolloSchema
and downloadApolloSchema
now use paths relative to the root of the project (#3773, #3752)Apollo Kotlin adds two tasks to help to manage schemas: convertApolloSchema
and downloadApolloSchema
. These tasks are meant to be used from the commandline.
Previously, paths were interpreted using the current working directory with File(path)
. Unfortunately, this is unreliable because Gradle might change the current working directory in some conditions (see Gradle#13927 or Gradle#6074 for an example).
With 3.1.0 and onwards, paths, will be interpreted relative to the root project directory (project.rootProject.file(path)
):
# schema is now interpreted relative to the root project directory and
# not the current working directory anymore. This example assumes there
# is a 'app' module that applies the apollo plugin
./gradlew downloadApolloSchema \
--endpoint="https://your.domain/graphql/endpoint" \
--schema="app/src/main/graphql/com/example/schema.graphqls"
Many thanks to @dhritzkiv, @mune0903, @StylianosGakis, @AchrafAmil and @jamesonwilliams for their awesome contributions! You rock ๐ธ ๐ค !
reconnectWhen
suspend and pass attempt number (#3772).okHttpClient
(#3771)convertApolloSchema
and downloadApolloSchema
use path from the root of the project (#3773, #3752)This is the first stable release for Apollo Android 3 Apollo Kotlin 3 ๐!
There is documentation, a migration guide and a blog post.
In a nutshell, Apollo Kotlin 3 brings:
Feel free to ask questions 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).
3.0.0-rc03
:Compared to the previous RC, this version adds a few new convenience API and fixes 3 annoying issues.
๐ Many thanks to @mateuszkwiecinski, @schoeda and @fn-jt for all the feedback ๐
ApolloCall.operation
public (#3698)SubscriptionWsProtocolAdapter
(#3697)Operation.composeJsonRequest
(#3697)@fieldPolicy
(#3686)๐ Many thanks to @michgauz, @joeldenke, @rohandhruva, @schoeda, @CoreFloDev and @sproctor for all the feedback ๐
In order to simplify the API and keep the symmetry with ApolloRequest<D>
and ApolloResponse<D>
, ApolloQueryCall<D, E>
, ApolloSubscriptionCall<D, E>
, ApolloMutationCall<D, E>
are replaced with ApolloCall<D>
. This change should be mostly transparent but it's technically a breaking change. If you are passing ApolloQueryCall<D, E>
variables, it is safe to drop the second type parameter and use ApolloCall<D>
instead.
WebSocketNetworkTransport.reconnectWhen {}
(#3674)You now have the option to reconnect a WebSocket automatically when an error happens and re-subscribe automatically after the reconnection has happened. To do so, use the webSocketReconnectWhen
parameter:
val apolloClient = ApolloClient.Builder()
.httpServerUrl("http://localhost:8080/graphql")
.webSocketServerUrl("http://localhost:8080/subscriptions")
.wsProtocol(
SubscriptionWsProtocol.Factory(
connectionPayload = {
mapOf("token" to upToDateToken)
}
)
)
.webSocketReconnectWhen {
// this is called when an error happens on the WebSocket
it is ApolloWebSocketClosedException && it.code == 1001
}
.build()
The HttpBatchingEngine
has been moved to an HttpInterceptor
. You can now configure Http batching with a specific method:
apolloClient = ApolloClient.Builder()
.serverUrl(mockServer.url())
.httpBatching(batchIntervalMillis = 10)
.build()
Rx2Apollo
, prefetch()
, customAttributes()
, ApolloIdlingResource.create()
) to help the transition (#3679)WebSocketNetworkTransport.reconnectWhen {}
(#3674)Published by BoD almost 3 years ago
This version is the release candidate for Apollo Android 3 ๐. Please try it and report any issues, we'll fix them urgently.
There is documentation and a migration guide. More details are coming soon. In a nutshell, Apollo Android 3 brings, amongst other things:
Compared to beta05
, this version changes the default value of generateOptionalOperationVariables
, is compatible with Gradle configuration cache and fixes a few other issues.
The default value for the generateOptionalOperationVariables
config is now true
.
What this means:
Optional
parametersFor instance:
query GetTodos($first: Int, $offset: Int) {
todos(first: $first, offset: $offset) {
...Todo
}
}
// Before
val query = GetTodosQuery(100, null)
// After
val query = GetTodosQuery(Optional.Present(100), Optional.Absent)
generateOptionalOperationVariables
to false
to generate non-optional parameters globally@optional
directiveWe think this change will make more sense to the majority of users (and is consistent with Apollo Android v2's behavior) even though it may be more verbose, which is why it is possible to change the behavior via the generateOptionalOperationVariables
config.
To keep the beta05
behavior, set generateOptionalOperationVariables
to false in your Gradle configuration:
apollo {
generateOptionalOperationVariables.set(false)
}
You can now pass WebSocket related options to the ApolloClient.Builder
directly (previously this would have been done via NetworkTransport
):
// Before
val apolloClient = ApolloClient.Builder()
// (...)
.subscriptionNetworkTransport(WebSocketNetworkTransport(
serverUrl = "https://example.com/graphql",
idleTimeoutMillis = 1000L,
wsProtocol = SubscriptionWsProtocol.Factory()
))
.build()
// After
val apolloClient = ApolloClient.Builder()
// (...)
.wsProtocol(SubscriptionWsProtocol.Factory())
.webSocketIdleTimeoutMillis(1000L)
.build()
This version upgrades OkHttp to 4.9.3
(from 3.12.11
). This means Apollo Android now requires Android apiLevel
21
+. As OkHttp 3 enters end of life at the end of the year and the vast majority of devices now support apiLevel
21
, we felt this was a reasonable upgrade.
Published by BoD almost 3 years ago
We intended this version to be the first release candidate but we found a few bugs justifying a new beta instead. Many thanks to @CoreFloDev, @olivierg13, @rohandhruva, @adavis and @sproctor for identifying the bugs and sending feedback, this helps a ton ๐ ๐ .
Next one should be release candidate ๐ค ๐
The escaping of Kotlin keywords used in field names now uses Kotlin's backtick escaping (`keyword`
) convention instead of suffixing an underscore (keyword_
). This could impact the generated code in your projects if a schema contains such fields. (#3630)
Before going stable, the public API has been tweaked to ensure consistency.
Upload
creation (#3613)Creating an Upload used to be possible by calling Upload.fromXyz()
methods and now has been changed to use a Builder API which is more consistent with the rest of the API:
// Before
val upload = Upload.fromSource(okioSource)
// or if you're on the JVM
val upload = Upload.fromFile(file)
// After
val upload = DefaultUpload.Builder()
.content(bufferedSource)
// or if you're on the JVM
.content(file)
.build()
The method to enable or disable Automatic Persisted Queries on a specific call has been renamed to a more meaningful name:
// Before
val response = apolloClient.query(HeroNameQuery())
.hashedQuery(false)
.execute()
// After
val response = apolloClient.query(HeroNameQuery())
.enableAutoPersistedQueries(false)
.execute()
During the course of the alpha and beta, a number of APIs have been evolving, and to allow a smooth transition by early adopters, some early APIs have been marked as deprecated in favor of new ones.
Now that we are soon reaching a stable API, it is time to remove these early APIs.
This means if you were using 3.0.0-beta04
or earlier you may want to first review the deprecation warnings and update your code to use the newer APIs, before upgrading to this version.
ApolloResponse
, you can now do so by using a Builder API (#3608)BufferedSource
as input. (#3620)HttpEngine
(OkHttpEngine
, NSURLSessionHttpEngine
, and KtorHttpEngine
) have all been renamed to DefaultHttpEngine
for consistency (#3617)// Before
String result = StringAdapter.INSTANCE.fromJson(jsonReader,CustomScalarAdapters.Empty);
// After
String result = Adapters.StringAdapter.fromJson(jsonReader,CustomScalarAdapters.Empty);
IOException
in Adapter APIs, making it possible to catch it in Java code (#3603)Published by BoD almost 3 years ago
This version is planned to be the last beta of Apollo Android 3 before a release candidate ๐. It mainly focuses on stabilization and tweaks to the public API, makes it easier to work with the Java9 module system, and fixes a few issues.
Before going stable, we tweaked the public API to ensure consistency. The following methods have been renamed:
Before | After |
---|---|
ApolloClient.mutate() |
ApolloClient.mutation() |
ApolloClient.subscribe() |
ApolloClient.subscription() |
ApolloSubscriptionCall.execute() |
ApolloSubscriptionCall.toFlow() |
In order to better support Java 9 and remove split packages, the following classes have been moved:
Before | After |
---|---|
com.apollographql.apollo3.cache.ApolloCacheHeaders |
com.apollographql.apollo3.cache.normalized.api.ApolloCacheHeaders |
com.apollographql.apollo3.cache.CacheHeaders |
com.apollographql.apollo3.cache.normalized.api.CacheHeaders |
com.apollographql.apollo3.cache.CacheHeaders.Builder |
com.apollographql.apollo3.cache.normalized.api.CacheHeaders.Builder |
com.apollographql.apollo3.cache.CacheHeaders.Companion |
com.apollographql.apollo3.cache.normalized.api.CacheHeaders.Companion |
com.apollographql.apollo3.cache.normalized.CacheKey |
com.apollographql.apollo3.cache.normalized.api.CacheKey |
com.apollographql.apollo3.cache.normalized.CacheKey.Companion |
com.apollographql.apollo3.cache.normalized.api.CacheKey.Companion |
com.apollographql.apollo3.cache.normalized.CacheKeyResolver |
com.apollographql.apollo3.cache.normalized.api.CacheKeyResolver |
com.apollographql.apollo3.cache.normalized.CacheResolver |
com.apollographql.apollo3.cache.normalized.api.CacheResolver |
com.apollographql.apollo3.cache.normalized.DefaultCacheResolver |
com.apollographql.apollo3.cache.normalized.api.DefaultCacheResolver |
com.apollographql.apollo3.cache.normalized.FieldPolicyCacheResolver |
com.apollographql.apollo3.cache.normalized.api.FieldPolicyCacheResolver |
com.apollographql.apollo3.cache.normalized.NormalizedCache.Companion |
com.apollographql.apollo3.cache.normalized.api.NormalizedCache.Companion |
com.apollographql.apollo3.cache.normalized.NormalizedCacheFactory |
com.apollographql.apollo3.cache.normalized.api.NormalizedCacheFactory |
com.apollographql.apollo3.cache.normalized.ReadOnlyNormalizedCache |
com.apollographql.apollo3.cache.normalized.api.ReadOnlyNormalizedCache |
com.apollographql.apollo3.cache.normalized.Record |
com.apollographql.apollo3.cache.normalized.api.Record |
com.apollographql.apollo3.cache.normalized.RecordFieldJsonAdapter |
com.apollographql.apollo3.cache.normalized.api.RecordFieldJsonAdapter |
com.apollographql.apollo3.cache.normalized.MemoryCache |
com.apollographql.apollo3.cache.normalized.api.MemoryCache |
com.apollographql.apollo3.cache.normalized.MemoryCacheFactory |
com.apollographql.apollo3.cache.normalized.api.MemoryCacheFactory |
com.apollographql.apollo3.cache.normalized.OperationCacheExtensionsKt |
com.apollographql.apollo3.cache.normalized.api.OperationCacheExtensionsKt |
Other renames
Before | After |
---|---|
com.apollographql.apollo3.api.ApolloExperimental |
com.apollographql.apollo3.annotations.ApolloExperimental |
com.apollographql.apollo3.api.ApolloInternal |
com.apollographql.apollo3.annotations.ApolloInternal |
com.apollographql.apollo3.cache.normalized.ObjectIdGenerator |
com.apollographql.apollo3.cache.normalized.api.CacheKeyGenerator |
com.apollographql.apollo3.cache.normalized.ObjectIdGeneratorContext |
com.apollographql.apollo3.cache.normalized.api.CacheKeyGeneratorContext |
com.apollographql.apollo3.network.http.OkHttpEngine |
com.apollographql.apollo3.network.http.DefaultHttpEngine |
ApolloClient
's mutate
and subscribe
methods have been renamed to mutation
and subscription
respectively. This
creates a more consistent API by reusing the GraphQL specification terminology.
Also, mutate()
, subscribe()
could be misleading as despite being verbs, no action is done until execute()
is
called. Using mutation()
/subscription()
hints that execute()
is required as a terminal operation.
// Before
val data = client.mutate(MyMutation()).execute()
// After
val data = client.mutation(MyMutation()).execute()
Also, ApolloSubscriptionCall.execute()
has been renamed to ApolloSubscriptionCall.toFlow()
to make it clearer that it
returns a cold Flow
that can be collected several times:
// Before
val flow = client.subscription(MySubscription).execute()
// After
val flow = client.subscription(MySubscription).toFlow()
CacheKeys
were sometimes referred to as Cache ids
, Object ids
, despite all of them being the same concept (the key
of an object in the cache). For consistency inside the Apollo Android codebase and with the iOS and Web
clients, CacheKey
is going to be used everywhere moving forward
// Before
val objectIdGenerator = object : ObjectIdGenerator {
override fun cacheKeyForObject(obj: Map<String, Any?>, context: ObjectIdGeneratorContext): CacheKey? {
// Retrieve the id from the object itself
return CacheKey(obj["id"] as String)
}
}
val apolloClient = ApolloClient.Builder()
.serverUrl("https://...")
.normalizedCache(
normalizedCacheFactory = cacheFactory,
objectIdGenerator = objectIdGenerator,
)
.build()
// After
val cacheKeyGenerator = object : CacheKeyGenerator {
override fun cacheKeyForObject(obj: Map<String, Any?>, context: CacheKeyGeneratorContext): CacheKey? {
// Retrieve the id from the object itself
return CacheKey(obj["id"] as String)
}
}
val apolloClient = ApolloClient.Builder()
.serverUrl("https://...")
.normalizedCache(
normalizedCacheFactory = cacheFactory,
cacheKeyGenerator = cacheKeyGenerator,
)
.build()
dataAssertNoErrors
(#3534)To reflect more accurately what this method does, dataOrThrow
is now dataAssertNoErrors
.
Apollo Android can either generate GraphQL enums as Kotlin enums or sealed classes. In previous 3.0 releases, the
default was to generate sealed classes but it is now enums, as this will be more expected, works better with Swift, and
is consistent with the 2.x behavior. To fallback to the previous behaviour of always generating sealed classes, use the sealedClassesForEnumsMatching
Gradle option:
apollo {
sealedClassesForEnumsMatching.set(listOf(".*"))
}
Using Apollo Android from Java should be less cumbersome thanks to a few API tweaks in this release. For example, to set
up a normalized cache:
// Before
builder = ClientCacheExtensionsKt.normalizedCache(
builder,
new MemoryCacheFactory(10 * 1024 * 1024, -1),
TypePolicyObjectIdGenerator.INSTANCE,
FieldPolicyCacheResolver.INSTANCE,
false
);
// After
NormalizedCache.configureApolloClientBuilder(
builder,
new MemoryCacheFactory(10 * 1024 * 1024, -1),
);
HttpNetworkTransport
and WebSocketNetworkTransport
now use the Builder pattern for instantiationFlowDecorator
have been removed from ApolloClient
as ApolloInterceptor
can be used insteadhttpHeader
extension is now addHttpHeader
Apollo Android now publishes jar files containing the Automatic-Module-Name
property and non-split packages. This
change makes it easier to work with Apollo Android from a Java 9+ modularized application.
You no longer need to register any of the built-in adapters after declaring a custom scalar type that maps to a built-in
type (Int, Long, String, etc.).
For instance, when declaring this custom scalar type:
apollo {
customScalarsMapping = [
"MyCustomNumber": "kotlin.Long"
]
}
There is no need to call addCustomScalarAdapter
on the builder anymore, as since it is for a kotlin.Long
it will
automatically fall back to the built-in LongAdapter
.
Published by martinbonnin almost 3 years ago
Version 2.5.11
is a maintenance release with fixes for registerApolloOperations
and query batching.
๐ Many thanks to @AOrobator and @rocketraman for their awesome contributions and feedback !๐
Full Changelog: https://github.com/apollographql/apollo-android/compare/v2.5.10...v2.5.11