Version 3.0.0-beta01
is the first Beta release for Apollo Android 3 ๐. While there are no API stability guarantees just yet, 3.0.0-beta01
introduces binary compatibility validation to monitor the breaking changes and they should happen less frequently from now on.
One important API change in 3.0.0-beta01
is the change from with-ers to Builders. It also has a useVersion2Compat Gradle property to ease the transition from 2.x.
In addition, 3.0.0-beta01
introduces JavaScript runtime and cache support and new Test Builders APIs to generate fake data models.
๐ Many thanks to @Pitel and @dchappelle for the awesome additions to this release !๐
โจ[new] JavaScript runtime and cache support (#3208)
Version 3.0.0-beta01
has support for JavaScript targets courtesy of @Pitel. It contains both IR and LEGACY artifacts. To use it in your builds, add apollo-runtime
and apollo-normalized-cache
dependencies to your build.gradle[.kts]
:
kotlin {
js(IR) { // or js(LEGACY)
sourceSets {
val commonMain by getting {
// To use HTTP and runtime
implementation("com.apollographql.apollo3:apollo-runtime:3.0.0-beta01")
// To use in-memory cache
implementation("com.apollographql.apollo3:apollo-normalized-cache:3.0.0-beta01")
}
}
}
}
This is everything needed. All the APIs work the same as their equivalent JVM/native ones.
Two caveats:
Contributions are very welcome, feel free to reach out on the kotlin lang slack to get started!
โจ[new] Test Builders API (#3415)
You can now opt-in generation of Test Builders that make it easier to build fake models for your operations. Test Builders allow to generate fake data using a type safe DSL and provides mock values for fields so that you don't have to specify them all.
To enable Test Builders, add the below to your Gradle scripts:
apollo {
generateTestBuilders.set(true)
}
This will generate builders and add them to your test sourceSets. You can use them to generate fake data:
// Import the generated TestBuilder
import com.example.test.SimpleQuery_TestBuilder.Data
@Test
fun test() {
// Data is an extension function that will build a SimpleQuery.Data model
val data = SimpleQuery.Data {
// Specify values for fields that you want to control
hero = droidHero {
name = "R2D2"
friends = listOf(
friend {
name = "Luke"
}
)
// leave other fields untouched, and they will be returned with mocked data
// planet = ...
}
}
// Use the returned data
}
You can control the returned mock data using the TestResolver API:
val myTestResolver = object: DefaultTestResolver() {
fun resolveInt(path: List<Any>): Int {
// Always return 42 in fake data for Int fields
return 42
}
}
val data = SimpleQuery.Data(myTestResolver) {}
// Yay, now every Int field in `data` is 42!
โจ๐งโจ [new and breaking] Version2 compatibility
3.0.0-beta01
introduces new Gradle options for better compatibility with versions 2. Most of the changes in this section can be reverted through configuration options but some had breaking side effects like valueOf
being renamed to safeValueOf
for enums.
sealedClassesForEnumsMatching
allows generating Kotlin enums for GraphQL enums.
Apollo 3.x generates sealed classes for Kotlin enums. As paradoxical as it may seem, sealed classes a better representation of GraphQL enums because they allow to expose the rawValue
of new enums that are not know at compile time. Sealed classes can also handle when
exhaustivity just like Kotlin enums and are generally more flexible. Using them may change the calling code though so as a temporary migration helper, you can now fallback to enum like in 2.x by setting sealedClassesForEnumsMatching
to an empty list instead of the default listOf(".*")
:
apollo {
sealedClassesForEnumsMatching.set(emptyList())
}
One side effect of this change is that the generated MySealedClass.valueOf()
has been renamed to MySealedClass.safeValueOf()
.
generateOptionalOperationVariables
allows wrapping your variables in Optional<>
.
By default Apollo Android 3 skips the Optional<>
wrapper for nullable variables. This simplifies the call site in the vast majority of cases where the variable is actually sent alongside the query.
There might be some rare occasions where you want to be able to omit a variable. For these cases, you can add an @optional
directive:
# a query that allows omitting before and/or after for bi-directional pagination
query MyQuery($before: String @optional, $after: String @optional) {
items {
title
}
}
If you have a lot of those queries, or if you prefer the 2.x behaviour, you can now opt-out globally:
apollo {
generateOptionalOperationVariables.set(true)
}
codegenModels
defaults to "operationBased"
3.0.0-beta01
now defaults to "operationBased"
models. "operationBased"
models match your GraphQL operations 1:1 and skip the extra .fragment
synthetic fields that are present in "compat"
models. Because they are simpler to understand, generate and execute, they are now the default. You can revert to "compat"
codegen with the codegenModels
Gradle option:
apollo {
codegenModels.set(MODELS_COMPAT)
}
useVersion2Compat()
For all these options, you can now fallback to the 2.x behaviour with useVersion2Compat()
. This is a shorthand function that configures the above options to match the 2.x behaviour. useVersion2Compat
is a helper to facilitate the migration and will be removed in a future update.
๐ง[breaking] ApolloClient and ApolloRequest Builder APIs
Following the with-er vs Builder vs DSL RFC, we decided to move the main APIs to Builders. Builders are widely accepted, battle proven APIs that play nicely with Java and will make it easier to maintain Apollo Android in the long run.
While this beta-01
release keeps the with-ers
, they will be removed before Apollo Android 3 goes stable so now is a good time to update.
To build an ApolloClient
:
// Replace
val apolloClient = ApolloClient("https://com.example/graphql")
.withNormalizedCache(normalizedCacheFactory)
// With
val apolloClient = ApolloClient.Builder()
.serverUrl("https://com.example/graphql")
.normalizedCache(normalizedCacheFactory)
.build()
To build a ApolloRequest
:
// Replace
val apolloRequest = ApolloRequest(query)
.withFetchPolicy(FetchPolicy.CacheFirst)
// With
val apolloRequest = ApolloRequest.Builder(query)
.fetchPolicy(FetchPolicy.CacheFirst)
.build()
Websocket updates
The WebSocket code has been revamped to support client-initiated ping-pong for graphql-ws as well as a better separation between common code and protocol specific code.
A side effect is that WebSocket protocols are now configured using a WsProtocol.Factory:
// Replace
val apolloClient = ApolloClient(
networkTransport = WebSocketNetworkTransport(
serverUrl = "http://localhost:9090/graphql",
protocol = GraphQLWsProtocol()
)
)
// With
val apolloClient = ApolloClient.Builder()
.networkTransport(
WebSocketNetworkTransport(
serverUrl = "http://localhost:9090/graphql",
protocolFactory = GraphQLWsProtocol.Factory()
)
)
.build()
๐ท All Changes
- Mutations over WebSocket (graphql-ws protocol) may hang indefinitely โฆ by @dchappelle in #3383
- Fix nanosecond conversion on native by @martinbonnin in #3386
- Support Ping/Pong messages for graphql-transport-ws (#3291) by @dchappelle in #3401
- WebSockets: better separate common code and protocol-specific code by @martinbonnin in #3405
- Bump SqlDelight by @martinbonnin in #3407
- Bump Kotlin to 1.5.31 by @martinbonnin in #3414
- Allow isFromCache on all ApolloResponses by @martinbonnin in #3416
- Update ApolloClient and ApolloRequest APIs from With-ers to Builders by @BoD in #3404
- JavaScript by @Pitel in #3208
- Add Test Builders by @martinbonnin in #3415
- Reintroduce sealedClassesForEnumsMatching by @martinbonnin in #3435
- Add generateOptionalOperationVariables gradle param by @martinbonnin in #3438
- Add ApolloClient.builder() method for v2 compat by @BoD in #3441
New Contributors
Full Changelog: v3.0.0-alpha07...v3.0.0-beta01