于10月8日至10日在纽约市与我们一起,了解关于 GraphQL Federation 和 API 平台工程的最新的技巧、趋势和新闻。参加2024年在纽约市举办的 GraphQL Summit
文档
免费开始
您正在查看该软件之前的版本文档。 切换到最新稳定版本。

基于响应的代码生成


尝试处理您的 操作,为其生成Kotlin模型,并在您的JSON响应中实例化它们,允许您以类型安全的方式访问您的数据。

实际上有3个不同的领域在起作用

  • The GraphQL 领域: s
  • The Kotlin domain: models
  • The JSON domain: responses

默认情况下, Apollo Kotlin 生成的模型与您的 GraphQL 操作 一一对应。内联和命名的 生成合成 ,因此您可以使用Kotlin代码data.hero.onDroid.primaryFunction. 是可以在不同 operations 中重用的类。这个代码生成引擎codegen被称为 operationBased,因为它与GraphQL operation 相匹配。

JSON响应的形状可能与您的 GraphQL 操作不同。这是使用合并字段片段时的情况。如果您想以JSON响应中的样子访问Kotlin属性,Apollo Kotlin提供了与JSON响应一一对应的responseBased代码生成器。GraphQL片段以Kotlin接口的形式表示,因此您可以使用Kotlin代码访问其字段,例如(data.hero as Droid).primaryFunction。因为它们映射到JSON响应,responseBased模型具有允许JSON流式传输和/或映射到动态JS对象的属性。但鉴于GraphQL是一种非常灵活的语言,创建一个生成非常大的JSON响应的GraphQL查询也是很容易的

因此以及其他限制,我们建议默认使用operationBased代码生成器

此页首先回顾operationBased代码生成器的工作原理,然后再解释responseBased代码生成器。最后,列出了使用responseBased代码生成器的不同限制,以便您在使用此代码生成器时做出明智的决定。

要使用特定的代码生成器,请在您的Gradle脚本中配置codegenModels

build.gradle.kts
apollo {
service("service") {
// ...
codegenModels.set("responseBased")
}
}

默认的operationBased

The operationBased代码生成器根据操作的形状生成模型。

  • 为每个复合选择生成一个模型。
  • 片段和内联片段作为自己的类生成。
  • 合并字段在查询时存储多次。

例如,给定这个

HeroQuery.graphql
query HeroForEpisode($ep: Episode!) {
search {
hero(episode: $ep) {
name
... on Droid {
name
primaryFunction
}
...HumanFields
}
}
}
fragment HumanFields on Human {
height
}

代码生成器生成以下类

HeroQuery.kt
class Search(
val hero: Hero?
)
class Hero(
val name: String,
val onDroid: OnDroid?,
val humanFields: HumanFields?
)
class OnDroid(
val name: String,
val primaryFunction: String
)
HumanFields.kt
class HumanFields(
val height: Double
)

注意onDroidhumanFieldsHero 类中是可空的。这是因为它们是否存在取决于返回的超级英雄的具体类型:

val hero = data.search?.hero
when {
hero.onDroid != null -> {
// Hero is a Droid
println(hero.onDroid.primaryFunction)
}
hero.humanFields != null -> {
// Hero is a Human
println(hero.humanFields.height)
}
else -> {
// Hero is something else
println(hero.name)
}
}

基于响应的代码生成器

与基于操作的代码生成器相比,基于响应的代码生成器有以下不同:

  • 生成的模型与接收在操作响应中的JSON结构的 1:1's 结构进行映射。
  • 多态通过生成 接口 处理。可能的形状定义为实现相应接口的不同类。
  • 片段也作为 接口 生成。
  • 任何合并的字段在生成模型中只出现 一次

让我们通过使用片段的示例来突出一些这些差异。

内联片段

考虑这个 查询

HeroQuery.graphql
query HeroForEpisode($ep: Episode!) {
hero(episode: $ep) {
name
... on Droid {
primaryFunction
}
... on Human {
height
}
}
}

如果在操作上运行基于响应的代码生成器,它会生成一个具有三个实现类的 Hero 接口:

  • DroidHero
  • HumanHero
  • OtherHero

由于 Hero 是具有不同实现接口,可以使用 when 表达式来处理每个不同的情况:

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)
}
}

访问器

作为便利性,基于响应的代码生成器生成命名模式为 as<ShapeName> 的方法(例如,asDroidasHuman),这使您无需手动转换:

val primaryFunction = hero1.asDroid().primaryFunction
val height = hero2.asHuman().height

命名片段

考虑这个示例

HeroQuery.graphql
query HeroForEpisode($ep: Episode!) {
hero(episode: $ep) {
name
...DroidFields
...HumanFields
}
}
fragment DroidFields on Droid {
primaryFunction
}
fragment HumanFields on Human {
height
}

基于响应的代码生成器为 DroidFieldsHumanFields 片段生成接口:

interface DroidFields {
val primaryFunction: String
}
interface HumanFields {
val height: Double
}

这些接口是由生成的 HeroForEpisodeQuery.Data.Hero 子类(及其他使用这些片段的任何操作模型)实现的:

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

可以这样使用

when (hero) {
is DroidFields -> println(hero.primaryFunction)
is HumanFields -> println(hero.height)
}

访问器

为了方便,responseBased 代码生成器会生成以模式命名的方法,即 <fragmentName>(例如,对于名为 DroidFields,其方法名为 droidFields)。这样您可以将调用链起来,如下所示:

val primaryFunction = hero1.droidFields().primaryFunction
val height = hero2.humanFields().height

responseBased 代码生成的限制

  1. 由于 GraphQL 是一种非常具有表现力的语言,因此很容易创建生成非常大的 JSON 响应的 GraphQL 查询。如果使用大量嵌套 fragments,生成的代码大小会随着嵌套层次的增加而指数级增长。我们已知一些相对较小的 GraphQL 查询会超过 JVM 的限制,如 最大方法大小
  2. 当使用 fragments 时,必须为使用 fragments 的每个操作生成数据类。为了避免名称冲突,模型是嵌套的,这带来两个副作用:
    • 生成的 .class 文件名可能会非常长,这在 MacOS 上会打破默认的 256 个字符的最大文件名限制
    • 类似的接口可能会嵌套(用于 fragments)。虽然这在 Kotlin 中是有效的,但 Java 不允许这样做,如果使用它,会导致 kapt 错误。
  3. @include@skip@defer 指令不被 fragmentsresponseBased 代码生成器上支持。支持它们需要在每次使用这些指令时生成两倍的模型。
上一页
Apollo AST
速率文章评分在GitHub上编辑编辑论坛Discord

版权所有 ©2024Apollo Graph Inc.,即 Apollo GraphQL.

隐私政策

公司