Skip to content

Latest commit

 

History

History
159 lines (120 loc) · 4.35 KB

response-based-codegen.mdx

File metadata and controls

159 lines (120 loc) · 4.35 KB
title
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 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:

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 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:

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, 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