Docs
Try Apollo Studio

Code generation methods in Apollo Kotlin


Apollo Kotlin provides multiple code generation engines (codegens) that you can choose from depending on your use case:

CodegenDescription
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 operationBased, models generated by the responseBased codegen are more performant and expose more type information, at the cost of generating more total code. See details.

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:

build.gradle.kts
apollo {
// ...
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:

HeroQuery.graphql
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 types
println(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().primaryFunction
val height = hero2.asHuman().height

Named fragments

Consider this example:

HeroQuery.graphql
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):

HeroForEpisodeQuery.kt
interface Hero {
val name: String
}
data class DroidHero(
override val name: String,
override val primaryFunction: String
) : Hero, DroidFields
data class HumanHero(
override val name: String,
override val height: Double
) : Hero, HumanFields
data 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().primaryFunction
val height = hero2.humanFields().height
Edit on GitHub
Previous
Client Awareness
Next
Using with Java