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 !๐
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!
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!
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.
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()
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()
Full Changelog: https://github.com/apollographql/apollo-android/compare/v3.0.0-alpha07...v3.0.0-beta01
Fetched April 11, 2026