Code generation methods in Apollo Kotlin
Apollo Kotlin provides multiple code generation engines (codegens) that you can choose from depending on your use case:
Codegen | Description |
---|---|
operationBased (default) | Generates models according to the shape of each defined operation. |
responseBased | Generates models according to the shape of each operation's response. Compared to |
compat (deprecated) | Similar to operationBased , except it generates models that are compatible with Apollo Android v2. This codegen helps projects upgrade from v2 to v3. |
💡 For more in-depth information on each codegen, see this design document.
To use a particular codegen, configure codegenModels
in your Gradle scripts:
apollo {service("service") {// ...codegenModels.set("responseBased")}}
The responseBased
codegen
The responseBased
codegen differs from the operationBased
codegen in the following ways:
- Generated models have a 1:1 mapping with the JSON structure received in an operation's response.
- Polymorphism is handled by generating interfaces. Possible shapes are then defined as different classes that implement the corresponding interfaces.
- Fragments are also generated as interfaces.
- Any merged fields appear once in generated models.
Let's look at examples using fragments to highlight some of these differences.
Inline fragments
Consider this query:
query HeroForEpisode($ep: Episode!) {hero(episode: $ep) {name... on Droid {primaryFunction}... on Human {height}}}
If we run the responseBased
codegen on this operation, it generates a Hero
interface with three implementing classes:
DroidHero
HumanHero
OtherHero
Because Hero
is an interface with different implementations, you can use a when
clause to handle each different case:
when (hero) {is DroidHero -> println(hero.primaryFunction)is HumanHero -> println(hero.height)else -> {// Account for other Hero types (including unknown ones)// Note: in this example `name` is common to all Hero typesprintln(hero.name)}}
Accessors
As a convenience, the responseBased
codegen generates methods with the name pattern as<ShapeName>
(e.g., asDroid
or asHuman
) that enable you to avoid manual casting:
val primaryFunction = hero1.asDroid().primaryFunctionval height = hero2.asHuman().height
Named fragments
Consider this example:
query HeroForEpisode($ep: Episode!) {hero(episode: $ep) {name...DroidFields...HumanFields}}fragment DroidFields on Droid {primaryFunction}fragment HumanFields on Human {height}
The responseBased
codegen generates interfaces for the DroidFields
and HumanFields
fragments:
interface DroidFields {val primaryFunction: String}interface HumanFields {val height: Double}
These interfaces are implemented by subclasses of the generated HeroForEpisodeQuery.Data.Hero
(and other models for any operations using
these fragments):
interface Hero {val name: String}data class DroidHero(override val name: String,override val primaryFunction: String) : Hero, DroidFieldsdata class HumanHero(override val name: String,override val height: Double) : Hero, HumanFieldsdata class OtherHero(override val name: String) : Hero
This can be used like so:
when (hero) {is DroidFields -> println(hero.primaryFunction)is HumanFields -> println(hero.height)}
Accessors
As a convenience, the responseBased
codegen generates methods with the name pattern <fragmentName>
(e.g., droidFields
for a fragment named DroidFields
). This enables you to chain calls together, like so:
val primaryFunction = hero1.droidFields().primaryFunctionval height = hero2.humanFields().height