Repository: apollographql/apollo-kotlin
Pull request #4359 (2022-08-26 13:18:47)

Allow custom scalars targets in FakeResolver return types
Previously, compile-time adapters required `resolveLeaf` to return a Json-like value. This break the symmetry with runtime adapters and is more cumbersome to use. 

To allow this, we now generate a `customScalarAdapters` property that contains all compile-time adapters that we can use to automatically serialize when needed.

Random thoughts: the compile-time vs run-time difference make all this code pretty cumbersome. In the future, we should think of ways to make everything a compile-time thing. Maybe with KSP:

```kotlin
@ApolloAdapter // annotation that will signal the adapter to use
object MyLongAdapter: Adapter<MyLong> {
  override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): MyLong {
    return MyLong(reader.nextString()!!.toLong())
  }

  override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: MyLong) {
    writer.value(value.value.toString())
  }
}
```

netlify[bot]: ### <span aria-hidden="true">👷</span> Deploy Preview for *apollo-android-docs* processing.


|  Name | Link |
|---------------------------------|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | a531f0b28f8ff1e124974d0857e97357509115df |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/apollo-android-docs/deploys/6308c838f72ec60007fcd3a9 |

Patch:
diff --git a/apollo-api/api/apollo-api.api b/apollo-api/api/apollo-api.api
index 35f514b682..993b03b199 100644
--- a/apollo-api/api/apollo-api.api
+++ b/apollo-api/api/apollo-api.api
@@ -402,7 +402,6 @@ public final class com/apollographql/apollo3/api/CustomScalarAdapters$Builder {
 	public final fun addAll (Lcom/apollographql/apollo3/api/CustomScalarAdapters;)Lcom/apollographql/apollo3/api/CustomScalarAdapters$Builder;
 	public final fun build ()Lcom/apollographql/apollo3/api/CustomScalarAdapters;
 	public final fun clear ()V
-	public final fun unsafe (Z)Lcom/apollographql/apollo3/api/CustomScalarAdapters$Builder;
 	public final fun variables (Lcom/apollographql/apollo3/api/Executable$Variables;)Lcom/apollographql/apollo3/api/CustomScalarAdapters$Builder;
 }
 
@@ -608,9 +607,9 @@ public final class com/apollographql/apollo3/api/FakeResolverContext {
 }
 
 public final class com/apollographql/apollo3/api/FakeResolverKt {
-	public static final fun buildData (Lcom/apollographql/apollo3/api/Adapter;Ljava/util/List;Ljava/lang/String;Ljava/util/Map;Lcom/apollographql/apollo3/api/FakeResolver;)Ljava/lang/Object;
-	public static final fun buildFragmentData (Lcom/apollographql/apollo3/api/Adapter;Ljava/util/List;Ljava/lang/String;Ljava/lang/Object;Lcom/apollographql/apollo3/api/FakeResolver;Lcom/apollographql/apollo3/api/CompiledType;)Ljava/lang/Object;
-	public static synthetic fun buildFragmentData$default (Lcom/apollographql/apollo3/api/Adapter;Ljava/util/List;Ljava/lang/String;Ljava/lang/Object;Lcom/apollographql/apollo3/api/FakeResolver;Lcom/apollographql/apollo3/api/CompiledType;ILjava/lang/Object;)Ljava/lang/Object;
+	public static final fun buildData (Lcom/apollographql/apollo3/api/Adapter;Ljava/util/List;Ljava/lang/String;Ljava/util/Map;Lcom/apollographql/apollo3/api/FakeResolver;Lcom/apollographql/apollo3/api/CustomScalarAdapters;)Ljava/lang/Object;
+	public static final fun buildFragmentData (Lcom/apollographql/apollo3/api/Adapter;Ljava/util/List;Ljava/lang/String;Ljava/lang/Object;Lcom/apollographql/apollo3/api/FakeResolver;Lcom/apollographql/apollo3/api/CompiledType;Lcom/apollographql/apollo3/api/CustomScalarAdapters;)Ljava/lang/Object;
+	public static synthetic fun buildFragmentData$default (Lcom/apollographql/apollo3/api/Adapter;Ljava/util/List;Ljava/lang/String;Ljava/lang/Object;Lcom/apollographql/apollo3/api/FakeResolver;Lcom/apollographql/apollo3/api/CompiledType;Lcom/apollographql/apollo3/api/CustomScalarAdapters;ILjava/lang/Object;)Ljava/lang/Object;
 }
 
 public final class com/apollographql/apollo3/api/FileUpload {
diff --git a/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/CompiledGraphQL.kt b/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/CompiledGraphQL.kt
index 424c151d48..ca3439234f 100644
--- a/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/CompiledGraphQL.kt
+++ b/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/CompiledGraphQL.kt
@@ -154,6 +154,12 @@ sealed class CompiledNamedType(val name: String) : CompiledType() {
   override fun leafType() = this
 }
 
+/**
+ * A GraphQL scalar type that is mapped to a Kotlin. This is named "Custom" for historical reasons
+ * but is also used for builtin scalars
+ * 
+ * TODO v4: rename this to ScalarType
+ */
 class CustomScalarType(
     /**
      * GraphQL schema custom scalar type name (e.g. `ID`, `URL`, `DateTime` etc.)
@@ -292,6 +298,9 @@ class EnumType(
   constructor(name: String): this(name, emptyList())
 }
 
+/**
+ * TODO v4: remove (see also [CustomScalarType] above
+ */
 class ScalarType(
     name: String,
 ) : CompiledNamedType(name)
diff --git a/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/CustomScalarAdapters.kt b/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/CustomScalarAdapters.kt
index 7a5a111721..4e461b25e7 100644
--- a/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/CustomScalarAdapters.kt
+++ b/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/CustomScalarAdapters.kt
@@ -3,6 +3,7 @@ package com.apollographql.apollo3.api
 import com.apollographql.apollo3.annotations.ApolloDeprecatedSince
 import com.apollographql.apollo3.annotations.ApolloDeprecatedSince.Version.v3_0_0
 import com.apollographql.apollo3.annotations.ApolloDeprecatedSince.Version.v3_2_1
+import com.apollographql.apollo3.annotations.ApolloExperimental
 import com.apollographql.apollo3.api.internal.Version2CustomTypeAdapterToAdapter
 import kotlin.jvm.JvmField
 
@@ -13,7 +14,7 @@ class CustomScalarAdapters private constructor(
     customScalarAdapters: Map<String, Adapter<*>>,
     // We piggyback CustomScalarAdapters to pass around a context which is used in the Adapters at parse time.
     // This is currently used for @skip/@include and @defer.
-    // Ideally it should be passed as its own parameter but we're avoiding a breaking change.
+    // Ideally it should be passed as its own parameter, but we're avoiding a breaking change.
     // See https://github.com/apollographql/apollo-kotlin/pull/3813
     val adapterContext: AdapterContext,
     private val unsafe: Boolean
@@ -78,6 +79,7 @@ class CustomScalarAdapters private constructor(
      * `MapJsonReader.nextValue` and `MapJsonWriter.value()`
      */
     @JvmField
+    @ApolloExperimental
     val PassThrough = Builder().unsafe(true).build()
   }
 
@@ -109,6 +111,7 @@ class CustomScalarAdapters private constructor(
       this.adaptersMap.putAll(customScalarAdapters.adaptersMap)
     }
 
+    @ApolloExperimental
     fun unsafe(unsafe: Boolean) = apply {
       this.unsafe = unsafe
     }
diff --git a/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/fakeResolver.kt b/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/fakeResolver.kt
index 755367f802..f8e5d484b2 100644
--- a/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/fakeResolver.kt
+++ b/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/fakeResolver.kt
@@ -29,11 +29,7 @@ interface FakeResolver {
    * - Int
    * - Double
    * - Strings
-   * - Custom scalar targets (see below)
-   *
-   * Custom scalars: for custom scalars whose adapter is registered at build time, [resolveLeaf] **must**
-   * return a Json representation of the scalar (using Map, List, Boolean, Int, Double, String).
-   * Target instances such as `Date`, `BigDecimal`, etc... are not supported because they would require [CustomScalarAdapters] to decode.
+   * - Custom scalar targets
    */
   fun resolveLeaf(context: FakeResolverContext): Any
 
@@ -96,6 +92,7 @@ private fun buildFakeObject(
     typename: String,
     base: Map<String, Any?>,
     resolver: FakeResolver,
+    customScalarAdapters: CustomScalarAdapters
 ): Map<String, Any?> {
   @Suppress("UNCHECKED_CAST")
   return buildFieldOfType(
@@ -105,7 +102,8 @@ private fun buildFakeObject(
           .build(),
       resolver,
       Optional.Present(base),
-      CompiledNotNullType(ObjectType.Builder(typename).build())
+      CompiledNotNullType(ObjectType.Builder(typename).build()),
+      customScalarAdapters
   ) as Map<String, Any?>
 }
 
@@ -114,8 +112,9 @@ private fun buildField(
     mergedField: CompiledField,
     resolver: FakeResolver,
     parent: Map<String, Any?>,
+    customScalarAdapters: CustomScalarAdapters
 ): Any? {
-  return buildFieldOfType(path, mergedField, resolver, parent.getOrAbsent(mergedField.responseName), mergedField.type)
+  return buildFieldOfType(path, mergedField, resolver, parent.getOrAbsent(mergedField.responseName), mergedField.type, customScalarAdapters)
 }
 
 private fun Map<String, Any?>.getOrAbsent(key: String) = if (containsKey(key)) {
@@ -130,24 +129,25 @@ private fun buildFieldOfType(
     resolver: FakeResolver,
     value: Optional<Any?>,
     type: CompiledType,
+    customScalarAdapters: CustomScalarAdapters,
 ): Any? {
   if (type !is CompiledNotNullType) {
     return if (value is Optional.Present) {
       if (value.value == null) {
         null
       } else {
-        buildFieldOfType(path, mergedField, resolver, value, CompiledNotNullType(type))
+        buildFieldOfType(path, mergedField, resolver, value, CompiledNotNullType(type), customScalarAdapters)
       }
     } else {
       if (resolver.resolveMaybeNull(FakeResolverContext(path, mergedField))) {
         null
       } else {
-        buildFieldOfType(path, mergedField, resolver, value, CompiledNotNullType(type))
+        buildFieldOfType(path, mergedField, resolver, value, CompiledNotNullType(type), customScalarAdapters)
       }
     }
   }
 
-  return buildFieldOfNonNullType(path, mergedField, resolver, value, type.ofType)
+  return buildFieldOfNonNullType(path, mergedField, resolver, value, type.ofType, customScalarAdapters)
 }
 
 private fun buildFieldOfNonNullType(
@@ -156,17 +156,18 @@ private fun buildFieldOfNonNullType(
     resolver: FakeResolver,
     value: Optional<Any?>,
     type: CompiledType,
+    customScalarAdapters: CustomScalarAdapters
 ): Any? {
   return when (type) {
     is CompiledListType -> {
       if (value is Optional.Present) {
         val list = (value.value as? List<Any?>) ?: error("")
         list.mapIndexed { index, item ->
-          buildFieldOfType(path + index, mergedField, resolver, Optional.Present(item), type.ofType)
+          buildFieldOfType(path + index, mergedField, resolver, Optional.Present(item), type.ofType, customScalarAdapters)
         }
       } else {
         0.until(resolver.resolveListSize(FakeResolverContext(path, mergedField))).map {
-          buildFieldOfType(path + it, mergedField, resolver, Optional.Absent, type.ofType)
+          buildFieldOfType(path + it, mergedField, resolver, Optional.Absent, type.ofType, customScalarAdapters)
         }
       }
     }
@@ -179,7 +180,7 @@ private fun buildFieldOfNonNullType(
           val typename = (map["__typename"] as? String) ?: error("")
 
           collectAndMerge(mergedField.selections, typename).associate {
-            it.responseName to buildField(path + it.responseName, it, resolver, map)
+            it.responseName to buildField(path + it.responseName, it, resolver, map, customScalarAdapters)
           }
         } else {
           value.value
@@ -190,10 +191,29 @@ private fun buildFieldOfNonNullType(
           val map = mapOf("__typename" to typename)
 
           collectAndMerge(mergedField.selections, typename).associate {
-            it.responseName to buildField(path + it.responseName, it, resolver, map)
+            it.responseName to buildField(path + it.responseName, it, resolver, map, customScalarAdapters)
           }
         } else {
-          resolver.resolveLeaf(FakeResolverContext(path, mergedField))
+          val leafValue = resolver.resolveLeaf(FakeResolverContext(path, mergedField))
+          if (type is CustomScalarType) {
+            /**
+             * This might be a scalar with a build time registered adapter.
+             * If that's the case, we need to adapt before putting the value in the map
+             * so that subsequent adapters can deserialize
+             */
+            val adapter = try {
+              customScalarAdapters.responseAdapterFor<Any>(type)
+            } catch (e: Exception) {
+              null
+            }
+            if (adapter != null) {
+              adaptValue(adapter, leafValue)
+            } else {
+              leafValue
+            }
+          } else {
+            leafValue
+          }
         }
       }
     }
@@ -263,9 +283,10 @@ fun <T> buildData(
     typename: String,
     map: Map<String, Any?>,
     resolver: FakeResolver,
+    customScalarAdapters: CustomScalarAdapters
 ): T {
   return adapter.obj(false).fromJson(
-      MapJsonReader(buildFakeObject(selections, typename, map, resolver)),
+      MapJsonReader(buildFakeObject(selections, typename, map, resolver, customScalarAdapters)),
       CustomScalarAdapters.PassThrough
   )
 }
@@ -276,7 +297,8 @@ fun <T> buildFragmentData(
     typename: String,
     block: Any? = null,
     resolver: FakeResolver,
-    type: CompiledType
+    type: CompiledType,
+    customScalarAdapters: CustomScalarAdapters
 ): T {
   val map = if (block == null) {
     mapOf(
@@ -298,6 +320,7 @@ fun <T> buildFragmentData(
       selections,
       typename,
       map,
-      resolver
+      resolver,
+      customScalarAdapters
   )
 }
diff --git a/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/http/DefaultHttpRequestComposer.kt b/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/http/DefaultHttpRequestComposer.kt
index 67313e7969..d07f6a4313 100644
--- a/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/http/DefaultHttpRequestComposer.kt
+++ b/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/http/DefaultHttpRequestComposer.kt
@@ -38,7 +38,8 @@ class DefaultHttpRequestComposer(
 
     val requestHeaders = listOf(
         HttpHeader(HEADER_APOLLO_OPERATION_ID, operation.id()),
-        HttpHeader(HEADER_APOLLO_OPERATION_NAME, operation.name())
+        HttpHeader(HEADER_APOLLO_OPERATION_NAME, operation.name()),
+        HttpHeader(HEADER_ACCEPT_NAME, HEADER_ACCEPT_VALUE),
     ) + (apolloRequest.httpHeaders ?: emptyList())
 
     val sendApqExtensions = apolloRequest.sendApqExtensions ?: false
@@ -78,6 +79,13 @@ class DefaultHttpRequestComposer(
     // for details.
     const val HEADER_APOLLO_OPERATION_NAME = "X-APOLLO-OPERATION-NAME"
 
+    private const val HEADER_ACCEPT_NAME = "Accept"
+
+    // TODO The deferSpec=20220822 part is a temporary measure so early backend implementations of the @defer directive
+    // can recognize early client implementations and potentially reply in a compaible way.
+    // This should be removed in later versions.
+    private const val HEADER_ACCEPT_VALUE = "multipart/mixed; deferSpec=20220822, application/json"
+
     private fun <D : Operation.Data> buildGetUrl(
         serverUrl: String,
         operation: Operation<D>,
diff --git a/apollo-ast/api/apollo-ast.api b/apollo-ast/api/apollo-ast.api
index 50a1259582..7e00762fff 100644
--- a/apollo-ast/api/apollo-ast.api
+++ b/apollo-ast/api/apollo-ast.api
@@ -1010,6 +1010,8 @@ public final class com/apollographql/apollo3/ast/GqlnodeKt {
 public final class com/apollographql/apollo3/ast/GqloperationdefinitionKt {
 	public static final fun rootTypeDefinition (Lcom/apollographql/apollo3/ast/GQLOperationDefinition;Lcom/apollographql/apollo3/ast/Schema;)Lcom/apollographql/apollo3/ast/GQLTypeDefinition;
 	public static final fun validate (Lcom/apollographql/apollo3/ast/GQLOperationDefinition;Lcom/apollographql/apollo3/ast/Schema;Ljava/util/Map;)Ljava/util/List;
+	public static final fun validate (Lcom/apollographql/apollo3/ast/GQLOperationDefinition;Lcom/apollographql/apollo3/ast/Schema;Ljava/util/Map;Z)Ljava/util/List;
+	public static synthetic fun validate$default (Lcom/apollographql/apollo3/ast/GQLOperationDefinition;Lcom/apollographql/apollo3/ast/Schema;Ljava/util/Map;ZILjava/lang/Object;)Ljava/util/List;
 }
 
 public final class com/apollographql/apollo3/ast/GqlstringsKt {
diff --git a/apollo-ast/src/main/kotlin/com/apollographql/apollo3/ast/api.kt b/apollo-ast/src/main/kotlin/com/apollographql/apollo3/ast/api.kt
index e4e270c79e..d1455b9afd 100644
--- a/apollo-ast/src/main/kotlin/com/apollographql/apollo3/ast/api.kt
+++ b/apollo-ast/src/main/kotlin/com/apollographql/apollo3/ast/api.kt
@@ -2,7 +2,6 @@
 
 package com.apollographql.apollo3.ast
 
-import com.apollographql.apollo3.annotations.ApolloDeprecatedSince
 import com.apollographql.apollo3.annotations.ApolloExperimental
 import com.apollographql.apollo3.annotations.ApolloInternal
 import com.apollographql.apollo3.ast.internal.ExecutableValidationScope
@@ -27,9 +26,9 @@ fun BufferedSource.toSchema(filePath: String? = null): Schema = parseAsGQLDocume
  * See [parseAsGQLDocument] and [validateAsExecutable] for more granular error reporting
  */
 @ApolloExperimental
-fun BufferedSource.toExecutableDefinitions(schema: Schema, filePath: String? = null): List<GQLDefinition> = parseAsGQLDocument(filePath)
+fun BufferedSource.toExecutableDefinitions(schema: Schema, filePath: String? = null, fieldsOnDisjointTypesMustMerge: Boolean = true): List<GQLDefinition> = parseAsGQLDocument(filePath)
     .valueAssertNoErrors()
-    .validateAsExecutable(schema)
+    .validateAsExecutable(schema, fieldsOnDisjointTypesMustMerge)
     .valueAssertNoErrors()
 
 /**
@@ -88,12 +87,15 @@ fun GQLDocument.validateAsSchemaAndAddApolloDefinition(): GQLResult<Schema> {
 /**
  * Validates the given document as an executable document.
  *
+ * @param fieldsOnDisjointTypesMustMerge set to false to relax the standard GraphQL [FieldsInSetCanMerge](https://spec.graphql.org/draft/#FieldsInSetCanMerge())
+ * and allow fields of different types at the same Json path as long as their parent types are disjoint.
+ *
  * @return  a [GQLResult] containing the operation and fragment definitions in 'value', along with any potential issues
  */
 @ApolloExperimental
-fun GQLDocument.validateAsExecutable(schema: Schema): GQLResult<List<GQLDefinition>> {
+fun GQLDocument.validateAsExecutable(schema: Schema, fieldsOnDisjointTypesMustMerge: Boolean = true): GQLResult<List<GQLDefinition>> {
   val fragments = definitions.filterIsInstance<GQLFragmentDefinition>().associateBy { it.name }
-  val issues = ExecutableValidationScope(schema, fragments).validate(this)
+  val issues = ExecutableValidationScope(schema, fragments, fieldsOnDisjointTypesMustMerge).validate(this)
   return GQLResult(definitions, issues)
 }
 
@@ -104,6 +106,7 @@ fun GQLDocument.validateAsExecutable(schema: Schema): GQLResult<List<GQLDefiniti
 fun GQLFragmentDefinition.inferVariables(
     schema: Schema,
     fragments: Map<String, GQLFragmentDefinition>,
-) = ExecutableValidationScope(schema, fragments).inferFragmentVariables(this)
+    fieldsOnDisjointTypesMustMerge: Boolean
+) = ExecutableValidationScope(schema, fragments, fieldsOnDisjointTypesMustMerge).inferFragmentVariables(this)
 
 
diff --git a/apollo-ast/src/main/kotlin/com/apollographql/apollo3/ast/gqloperationdefinition.kt b/apollo-ast/src/main/kotlin/com/apollographql/apollo3/ast/gqloperationdefinition.kt
index d1f0da5a45..0a1d64602d 100644
--- a/apollo-ast/src/main/kotlin/com/apollographql/apollo3/ast/gqloperationdefinition.kt
+++ b/apollo-ast/src/main/kotlin/com/apollographql/apollo3/ast/gqloperationdefinition.kt
@@ -9,7 +9,9 @@ fun GQLOperationDefinition.rootTypeDefinition(schema: Schema) = when (operationT
   else -> null
 }
 
+@JvmOverloads
 fun GQLOperationDefinition.validate(
     schema: Schema,
     fragments: Map<String, GQLFragmentDefinition>,
-) = ExecutableValidationScope(schema, fragments).validateOperation(this)
\ No newline at end of file
+    fieldsOnDisjointTypesMustMerge: Boolean = true,
+) = ExecutableValidationScope(schema, fragments, fieldsOnDisjointTypesMustMerge).validateOperation(this)
\ No newline at end of file
diff --git a/apollo-ast/src/main/kotlin/com/apollographql/apollo3/ast/internal/ExecutableValidationScope.kt b/apollo-ast/src/main/kotlin/com/apollographql/apollo3/ast/internal/ExecutableValidationScope.kt
index 97efa00b4e..89b436294e 100644
--- a/apollo-ast/src/main/kotlin/com/apollographql/apollo3/ast/internal/ExecutableValidationScope.kt
+++ b/apollo-ast/src/main/kotlin/com/apollographql/apollo3/ast/internal/ExecutableValidationScope.kt
@@ -44,10 +44,14 @@ import com.apollographql.apollo3.ast.sharesPossibleTypesWith
 /**
  * @param fragmentDefinitions: all the fragments in the current compilation unit.
  * This is required to check the type conditions as well as fields merging
+ *
+ * @param fieldsOnDisjointTypesMustMerge set to false to relax the standard GraphQL [FieldsInSetCanMerge](https://spec.graphql.org/draft/#FieldsInSetCanMerge())
+ * and allow fields of different types at the same Json path as long as their parent types are disjoint.
  */
 internal class ExecutableValidationScope(
     private val schema: Schema,
     private val fragmentDefinitions: Map<String, GQLFragmentDefinition>,
+    private val fieldsOnDisjointTypesMustMerge: Boolean,
 ) : ValidationScope {
   override val typeDefinitions = schema.typeDefinitions
   override val directiveDefinitions = schema.directiveDefinitions
@@ -240,7 +244,7 @@ internal class ExecutableValidationScope(
       validateDirective(it, this) {
         variableUsages.add(it)
       }
-      if (it.name == "experimental_defer" && !path.startsWith('-')) it.validateDeferDirective(selectionSetParent, path)
+      if (it.name == "defer" && !path.startsWith('-')) it.validateDeferDirective(selectionSetParent, path)
     }
   }
 
@@ -277,7 +281,7 @@ internal class ExecutableValidationScope(
       validateDirective(it, this) {
         variableUsages.add(it)
       }
-      if (it.name == "experimental_defer" && !path.startsWith('-')) it.validateDeferDirective(selectionSetParent, path)
+      if (it.name == "defer" && !path.startsWith('-')) it.validateDeferDirective(selectionSetParent, path)
     }
   }
 
@@ -637,6 +641,9 @@ internal class ExecutableValidationScope(
 
   // 5.3.2 2.1
   private fun haveSameResponseShape(fieldWithParentA: FieldWithParent, fieldWithParentB: FieldWithParent): Boolean {
+    if (!fieldsOnDisjointTypesMustMerge) {
+      return true
+    }
     val fieldA = fieldWithParentA.field
     val fieldB = fieldWithParentB.field
 
diff --git a/apollo-ast/src/main/resources/builtins.graphqls b/apollo-ast/src/main/resources/builtins.graphqls
index c62b6553ae..ea82981432 100644
--- a/apollo-ast/src/main/resources/builtins.graphqls
+++ b/apollo-ast/src/main/resources/builtins.graphqls
@@ -133,7 +133,7 @@ directive @deprecated(
   reason: String = "No longer supported"
 ) on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | ENUM_VALUE
 
-directive @experimental_defer(
+directive @defer(
   label: String
   if: Boolean
 ) on FRAGMENT_SPREAD | INLINE_FRAGMENT
diff --git a/apollo-compiler/api/apollo-compiler.api b/apollo-compiler/api/apollo-compiler.api
index 276d1be112..107f67370f 100644
--- a/apollo-compiler/api/apollo-compiler.api
+++ b/apollo-compiler/api/apollo-compiler.api
@@ -318,6 +318,7 @@ public final class com/apollographql/apollo3/compiler/codegen/ResolverKeyJsonAda
 public final class com/apollographql/apollo3/compiler/codegen/ResolverKeyKind : java/lang/Enum {
 	public static final field BuilderFun Lcom/apollographql/apollo3/compiler/codegen/ResolverKeyKind;
 	public static final field BuilderType Lcom/apollographql/apollo3/compiler/codegen/ResolverKeyKind;
+	public static final field CustomScalarAdapters Lcom/apollographql/apollo3/compiler/codegen/ResolverKeyKind;
 	public static final field Fragment Lcom/apollographql/apollo3/compiler/codegen/ResolverKeyKind;
 	public static final field FragmentSelections Lcom/apollographql/apollo3/compiler/codegen/ResolverKeyKind;
 	public static final field FragmentVariablesAdapter Lcom/apollographql/apollo3/compiler/codegen/ResolverKeyKind;
@@ -392,6 +393,7 @@ public final class com/apollographql/apollo3/compiler/codegen/kotlin/KotlinResol
 	public final fun getNext ()Lcom/apollographql/apollo3/compiler/codegen/kotlin/KotlinResolver;
 	public final fun registerBuilderFun (Ljava/lang/String;Lcom/squareup/kotlinpoet/MemberName;)V
 	public final fun registerBuilderType (Ljava/lang/String;Lcom/squareup/kotlinpoet/ClassName;)Lcom/squareup/kotlinpoet/ClassName;
+	public final fun registerCustomScalarAdapters (Lcom/squareup/kotlinpoet/ClassName;)V
 	public final fun registerEnumAdapter (Ljava/lang/String;Lcom/squareup/kotlinpoet/ClassName;)Lcom/squareup/kotlinpoet/ClassName;
 	public final fun registerFragment (Ljava/lang/String;Lcom/squareup/kotlinpoet/ClassName;)Lcom/squareup/kotlinpoet/ClassName;
 	public final fun registerFragmentSelections (Ljava/lang/String;Lcom/squareup/kotlinpoet/ClassName;)Lcom/squareup/kotlinpoet/ClassName;
@@ -410,6 +412,7 @@ public final class com/apollographql/apollo3/compiler/codegen/kotlin/KotlinResol
 	public final fun resolveBuilderFun (Ljava/lang/String;)Lcom/squareup/kotlinpoet/MemberName;
 	public final fun resolveBuilderType (Ljava/lang/String;)Lcom/squareup/kotlinpoet/ClassName;
 	public final fun resolveCompiledType (Ljava/lang/String;)Lcom/squareup/kotlinpoet/CodeBlock;
+	public final fun resolveCustomScalarAdapters ()Lcom/squareup/kotlinpoet/ClassName;
 	public final fun resolveFragment (Ljava/lang/String;)Lcom/squareup/kotlinpoet/ClassName;
 	public final fun resolveFragmentSelections (Ljava/lang/String;)Lcom/squareup/kotlinpoet/ClassName;
 	public final fun resolveFragmentVariablesAdapter (Ljava/lang/String;)Lcom/squareup/kotlinpoet/ClassName;
diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/ApolloCompiler.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/ApolloCompiler.kt
index 235f9f09ff..7221e3fae4 100644
--- a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/ApolloCompiler.kt
+++ b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/ApolloCompiler.kt
@@ -83,7 +83,7 @@ object ApolloCompiler {
     val validationResult = GQLDocument(
         definitions = definitions + incomingFragments,
         filePath = null
-    ).validateAsExecutable(options.schema)
+    ).validateAsExecutable(options.schema, options.fieldsOnDisjointTypesMustMerge)
 
     validationResult.issues.checkNoErrors()
 
@@ -160,7 +160,8 @@ object ApolloCompiler {
         scalarMapping = options.scalarMapping,
         codegenModels = options.codegenModels,
         generateOptionalOperationVariables = options.generateOptionalOperationVariables,
-        generateDataBuilders = options.generateDataBuilders
+        generateDataBuilders = options.generateDataBuilders,
+        fieldsOnDisjointTypesMustMerge = options.fieldsOnDisjointTypesMustMerge
     ).build()
 
     if (debugDir != null) {
diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/Options.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/Options.kt
index 01e9549fd4..2c1eb67abe 100644
--- a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/Options.kt
+++ b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/Options.kt
@@ -189,7 +189,19 @@ class Options(
      */
     val addJvmOverloads: Boolean = false,
     val addTypename: String = defaultAddTypename,
-    val requiresOptInAnnotation: String? = defaultRequiresOptInAnnotation
+    val requiresOptInAnnotation: String? = defaultRequiresOptInAnnotation,
+
+    /**
+     * Whether fields with different shape are disallowed to be merged in disjoint types.
+     *
+     * Note: setting this to `false` relaxes the standard GraphQL [FieldsInSetCanMerge](https://spec.graphql.org/draft/#FieldsInSetCanMerge()) validation which may still be
+     * run on the backend.
+     *
+     * See also [issue 4320](https://github.com/apollographql/apollo-kotlin/issues/4320)
+     *
+     * Default: true.
+     */
+    val fieldsOnDisjointTypesMustMerge: Boolean = defaultFieldsOnDisjointTypesMustMerge,
 ) {
 
   /**
@@ -247,7 +259,8 @@ class Options(
       generateOptionalOperationVariables: Boolean = this.generateOptionalOperationVariables,
       addJvmOverloads: Boolean = this.addJvmOverloads,
       addTypename: String = this.addTypename,
-      requiresOptInAnnotation: String? = this.requiresOptInAnnotation
+      requiresOptInAnnotation: String? = this.requiresOptInAnnotation,
+      fieldsOnDisjointTypesMustMerge: Boolean = this.fieldsOnDisjointTypesMustMerge
   ) = Options(
       executableFiles = executableFiles,
       schema = schema,
@@ -284,6 +297,7 @@ class Options(
       addJvmOverloads = addJvmOverloads,
       addTypename = addTypename,
       requiresOptInAnnotation = requiresOptInAnnotation,
+      fieldsOnDisjointTypesMustMerge = fieldsOnDisjointTypesMustMerge
   )
 
   companion object {
@@ -314,6 +328,7 @@ class Options(
     const val defaultGenerateOptionalOperationVariables = true
     const val defaultUseSchemaPackageNameForFragments = false
     const val defaultAddJvmOverloads = false
+    const val defaultFieldsOnDisjointTypesMustMerge = true
   }
 }
 
diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/ClassNames.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/ClassNames.kt
index 2bff59f0ba..2c134a0738 100644
--- a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/ClassNames.kt
+++ b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/ClassNames.kt
@@ -22,6 +22,7 @@ internal object ClassNames {
   val JsonReader = ResolverClassName(apolloApiJsonPackageName, "JsonReader")
   val JsonWriter = ResolverClassName(apolloApiJsonPackageName, "JsonWriter")
   val CustomScalarAdapters = ResolverClassName(apolloApiPackageName, "CustomScalarAdapters")
+  val CustomScalarAdaptersBuilder = ResolverClassName(apolloApiPackageName, "CustomScalarAdapters", "Builder")
   val Optional = ResolverClassName(apolloApiPackageName, "Optional")
   val Absent = ResolverClassName(apolloApiPackageName, "Optional", "Absent")
   val Present = ResolverClassName(apolloApiPackageName, "Optional", "Present")
diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/Resolver.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/Resolver.kt
index 2060b042f8..a4782dcdfe 100644
--- a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/Resolver.kt
+++ b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/Resolver.kt
@@ -33,7 +33,8 @@ enum class ResolverKeyKind {
   MapType,
   BuilderType,
   BuilderFun,
-  Schema
+  Schema,
+  CustomScalarAdapters
 }
 
 @JsonClass(generateAdapter = true)
diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/java/JavaClassNames.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/java/JavaClassNames.kt
index 58f504362a..2c860faa9d 100644
--- a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/java/JavaClassNames.kt
+++ b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/java/JavaClassNames.kt
@@ -23,6 +23,7 @@ internal object JavaClassNames {
   val JsonReader = ClassNames.JsonReader.toJavaPoetClassName()
   val JsonWriter = ClassNames.JsonWriter.toJavaPoetClassName()
   val CustomScalarAdapters = ClassNames.CustomScalarAdapters.toJavaPoetClassName()
+  val CustomScalarAdaptersBuilder = ClassNames.CustomScalarAdaptersBuilder.toJavaPoetClassName()
   val Optional = ClassNames.Optional.toJavaPoetClassName()
   val Absent = ClassNames.Absent.toJavaPoetClassName()
   val Present = ClassNames.Present.toJavaPoetClassName()
diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/java/JavaCodeGen.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/java/JavaCodeGen.kt
index 40c5a75415..87cc680a84 100644
--- a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/java/JavaCodeGen.kt
+++ b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/java/JavaCodeGen.kt
@@ -179,7 +179,7 @@ internal class JavaCodeGen(
         }
 
     if (generateSchema) {
-      builders.add(SchemaBuilder(context, generatedSchemaName, ir.objects, ir.interfaces, ir.unions, ir.enums))
+      builders.add(SchemaBuilder(context, generatedSchemaName, scalarMapping, ir.objects, ir.interfaces, ir.unions, ir.enums))
     }
     if (generateDataBuilders) {
       builders.add(BuilderFactoryBuilder(context, ir.objects))
diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/java/file/OperationBuilder.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/java/file/OperationBuilder.kt
index fe6f8a447e..89fe75e469 100644
--- a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/java/file/OperationBuilder.kt
+++ b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/java/file/OperationBuilder.kt
@@ -6,6 +6,7 @@ import com.apollographql.apollo3.compiler.codegen.Identifier
 import com.apollographql.apollo3.compiler.codegen.Identifier.OPERATION_DOCUMENT
 import com.apollographql.apollo3.compiler.codegen.Identifier.OPERATION_ID
 import com.apollographql.apollo3.compiler.codegen.Identifier.OPERATION_NAME
+import com.apollographql.apollo3.compiler.codegen.Identifier.customScalarAdapters
 import com.apollographql.apollo3.compiler.codegen.Identifier.document
 import com.apollographql.apollo3.compiler.codegen.Identifier.id
 import com.apollographql.apollo3.compiler.codegen.Identifier.name
@@ -160,7 +161,8 @@ internal class OperationBuilder(
                 .add("$T.$root,\n", context.resolver.resolveOperationSelections(operation.name))
                 .add("$S,\n", operation.operationType.typeName)
                 .add("${Identifier.map},\n")
-                .add("${Identifier.resolver}\n")
+                .add("${Identifier.resolver},\n")
+                .add("$T.$customScalarAdapters\n", context.resolver.resolveSchema())
                 .unindent()
                 .add(");")
                 .build()
diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/java/file/SchemaBuilder.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/java/file/SchemaBuilder.kt
index 14e1b06913..63e886490a 100644
--- a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/java/file/SchemaBuilder.kt
+++ b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/java/file/SchemaBuilder.kt
@@ -1,12 +1,16 @@
 package com.apollographql.apollo3.compiler.codegen.java.file
 
 
+import com.apollographql.apollo3.compiler.ExpressionAdapterInitializer
+import com.apollographql.apollo3.compiler.ScalarInfo
+import com.apollographql.apollo3.compiler.codegen.Identifier.customScalarAdapters
 import com.apollographql.apollo3.compiler.codegen.Identifier.type
 import com.apollographql.apollo3.compiler.codegen.Identifier.types
 import com.apollographql.apollo3.compiler.codegen.java.CodegenJavaFile
 import com.apollographql.apollo3.compiler.codegen.java.JavaClassBuilder
 import com.apollographql.apollo3.compiler.codegen.java.JavaClassNames
 import com.apollographql.apollo3.compiler.codegen.java.JavaContext
+import com.apollographql.apollo3.compiler.codegen.java.L
 import com.apollographql.apollo3.compiler.codegen.java.T
 import com.apollographql.apollo3.compiler.codegen.java.helpers.toListInitializerCodeblock
 import com.apollographql.apollo3.compiler.ir.IrEnum
@@ -24,6 +28,7 @@ import javax.lang.model.element.Modifier
 internal class SchemaBuilder(
     private val context: JavaContext,
     private val generatedSchemaName: String,
+    private val scalarMapping: Map<String, ScalarInfo>,
     private val objects: List<IrObject>,
     private val interfaces: List<IrInterface>,
     private val unions: List<IrUnion>,
@@ -63,11 +68,34 @@ internal class SchemaBuilder(
     return TypeSpec.classBuilder(generatedSchemaName)
         .addJavadoc("A Schema object containing all the composite types and a possibleTypes helper function")
         .addModifiers(Modifier.PUBLIC)
+        .addField(customScalarAdaptersFieldSpec())
         .addField(typesFieldSpec())
         .addMethod(possibleTypesFunSpec())
         .build()
   }
 
+  private fun customScalarAdaptersFieldSpec(): FieldSpec {
+    return FieldSpec.builder(JavaClassNames.CustomScalarAdapters, customScalarAdapters)
+        .initializer(
+            CodeBlock.builder()
+                .add("new $T()\n", JavaClassNames.CustomScalarAdaptersBuilder)
+                .indent()
+                .apply {
+                  scalarMapping.entries.forEach {
+                    val adapterInitializer = it.value.adapterInitializer
+                    if (adapterInitializer is ExpressionAdapterInitializer) {
+                      add(".add($T.type, $L)\n", context.resolver.resolveSchemaType(it.key), adapterInitializer.expression)
+                    }
+                  }
+                }
+                .add(".build()")
+                .unindent()
+                .build()
+        )
+        .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
+        .build()
+  }
+
   private fun possibleTypesFunSpec(): MethodSpec {
     val builder = MethodSpec.methodBuilder("possibleTypes")
     builder.addModifiers(Modifier.PUBLIC)
diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/CgFile.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/CgFile.kt
index 16857b6ece..24a09f18c2 100644
--- a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/CgFile.kt
+++ b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/CgFile.kt
@@ -2,6 +2,7 @@ package com.apollographql.apollo3.compiler.codegen.kotlin
 
 import com.squareup.kotlinpoet.FileSpec
 import com.squareup.kotlinpoet.FunSpec
+import com.squareup.kotlinpoet.PropertySpec
 import com.squareup.kotlinpoet.TypeSpec
 
 /**
@@ -10,6 +11,7 @@ import com.squareup.kotlinpoet.TypeSpec
 internal class CgFile(
     val packageName: String,
     val typeSpecs: List<TypeSpec> = emptyList(),
+    val propertySpecs: List<PropertySpec> = emptyList(),
     val funSpecs: List<FunSpec> = emptyList(),
     val fileName: String,
     val isTest: Boolean = false
diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/KotlinCodeGen.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/KotlinCodeGen.kt
index e403282899..60ea5a5a83 100644
--- a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/KotlinCodeGen.kt
+++ b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/KotlinCodeGen.kt
@@ -6,6 +6,7 @@ import com.apollographql.apollo3.compiler.ScalarInfo
 import com.apollographql.apollo3.compiler.TargetLanguage
 import com.apollographql.apollo3.compiler.codegen.ResolverInfo
 import com.apollographql.apollo3.compiler.codegen.kotlin.file.CustomScalarBuilder
+import com.apollographql.apollo3.compiler.codegen.kotlin.file.CustomScalarAdaptersBuilder
 import com.apollographql.apollo3.compiler.codegen.kotlin.file.EnumAsEnumBuilder
 import com.apollographql.apollo3.compiler.codegen.kotlin.file.EnumAsSealedBuilder
 import com.apollographql.apollo3.compiler.codegen.kotlin.file.EnumResponseAdapterBuilder
@@ -32,6 +33,7 @@ import com.apollographql.apollo3.compiler.operationoutput.findOperationId
 import com.squareup.kotlinpoet.FileSpec
 import com.squareup.kotlinpoet.FunSpec
 import com.squareup.kotlinpoet.KModifier
+import com.squareup.kotlinpoet.PropertySpec
 import com.squareup.kotlinpoet.TypeSpec
 import java.io.File
 
@@ -200,6 +202,7 @@ internal class KotlinCodeGen(
 
     if (generateSchema) {
       builders.add(SchemaBuilder(context, generatedSchemaName, ir.objects, ir.interfaces, ir.unions, ir.enums))
+      builders.add(CustomScalarAdaptersBuilder(context, scalarMapping))
     }
 
     if (ir.connectionTypes.isNotEmpty()) {
@@ -235,7 +238,9 @@ internal class KotlinCodeGen(
           cgFile.funSpecs.map { funSpec -> funSpec.internal(generateAsInternal) }.forEach { funSpec ->
             builder.addFunction(funSpec)
           }
-
+          cgFile.propertySpecs.map { propertySpec -> propertySpec.internal(generateAsInternal) }.forEach { propertySpec ->
+            builder.addProperty(propertySpec)
+          }
           val dir = if (cgFile.isTest) {
             testDir
           } else {
@@ -269,4 +274,12 @@ internal class KotlinCodeGen(
       this
     }
   }
+
+  private fun PropertySpec.internal(generateAsInternal: Boolean): PropertySpec {
+    return if (generateAsInternal) {
+      this.toBuilder().addModifiers(KModifier.INTERNAL).build()
+    } else {
+      this
+    }
+  }
 }
diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/KotlinResolver.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/KotlinResolver.kt
index f008b225d7..667a133f00 100644
--- a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/KotlinResolver.kt
+++ b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/KotlinResolver.kt
@@ -337,4 +337,10 @@ class KotlinResolver(
   }
 
   fun resolveSchema(): ClassName = resolveAndAssert(ResolverKeyKind.Schema, "")
+
+  fun registerCustomScalarAdapters(className: ClassName) {
+    register(ResolverKeyKind.CustomScalarAdapters, "", className)
+  }
+
+  fun resolveCustomScalarAdapters(): ClassName = resolveAndAssert(ResolverKeyKind.CustomScalarAdapters, "")
 }
diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/KotlinSymbols.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/KotlinSymbols.kt
index f6a8c4e9bc..419da2da21 100644
--- a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/KotlinSymbols.kt
+++ b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/KotlinSymbols.kt
@@ -26,6 +26,7 @@ internal object KotlinSymbols {
   val JsonReader = ClassNames.JsonReader.toKotlinPoetClassName()
   val JsonWriter = ClassNames.JsonWriter.toKotlinPoetClassName()
   val CustomScalarAdapters = ClassNames.CustomScalarAdapters.toKotlinPoetClassName()
+  val CustomScalarAdaptersBuilder = ClassNames.CustomScalarAdaptersBuilder.toKotlinPoetClassName()
   val Optional = ClassNames.Optional.toKotlinPoetClassName()
   val Absent = ClassNames.Absent.toKotlinPoetClassName()
   val Present = ClassNames.Present.toKotlinPoetClassName()
diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/file/CustomScalarAdaptersBuilder.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/file/CustomScalarAdaptersBuilder.kt
new file mode 100644
index 0000000000..5995656c41
--- /dev/null
+++ b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/file/CustomScalarAdaptersBuilder.kt
@@ -0,0 +1,53 @@
+package com.apollographql.apollo3.compiler.codegen.kotlin.file
+
+import com.apollographql.apollo3.compiler.ExpressionAdapterInitializer
+import com.apollographql.apollo3.compiler.ScalarInfo
+import com.apollographql.apollo3.compiler.codegen.kotlin.CgFile
+import com.apollographql.apollo3.compiler.codegen.kotlin.CgFileBuilder
+import com.apollographql.apollo3.compiler.codegen.kotlin.KotlinContext
+import com.apollographql.apollo3.compiler.codegen.kotlin.KotlinSymbols
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.CodeBlock
+import com.squareup.kotlinpoet.PropertySpec
+
+internal class CustomScalarAdaptersBuilder(
+    private val context: KotlinContext,
+    private val scalarMapping: Map<String, ScalarInfo>,
+) : CgFileBuilder {
+  private val layout = context.layout
+  private val packageName = layout.typePackageName()
+  private val simpleName = "__CustomScalarAdapters"
+
+  override fun prepare() {
+    context.resolver.registerCustomScalarAdapters(ClassName(packageName, simpleName))
+  }
+
+  override fun build(): CgFile {
+    return CgFile(
+        packageName = packageName,
+        fileName = simpleName,
+        propertySpecs = listOf(propertySpec())
+    )
+  }
+
+  private fun propertySpec(): PropertySpec {
+    return PropertySpec.builder(simpleName, KotlinSymbols.CustomScalarAdapters)
+        .initializer(
+            CodeBlock.builder()
+                .add("%T()\n", KotlinSymbols.CustomScalarAdaptersBuilder)
+                .indent()
+                .apply {
+                  scalarMapping.entries.forEach {
+                    val adapterInitializer = it.value.adapterInitializer
+                    if (adapterInitializer is ExpressionAdapterInitializer) {
+                      add(".add(%T.type, %L)\n", context.resolver.resolveSchemaType(it.key), adapterInitializer.expression)
+                    }
+                  }
+                }
+                .add(".build()\n")
+                .build()
+        )
+        .addKdoc("A [CustomScalarAdapters] instance containing all the adapters known at build time")
+        .build()
+  }
+}
diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/file/FragmentBuilder.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/file/FragmentBuilder.kt
index 69f6ee8dd6..386fd64f5d 100644
--- a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/file/FragmentBuilder.kt
+++ b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/file/FragmentBuilder.kt
@@ -1,9 +1,5 @@
 package com.apollographql.apollo3.compiler.codegen.kotlin.file
 
-import com.apollographql.apollo3.api.BuilderScope
-import com.apollographql.apollo3.api.DefaultFakeResolver
-import com.apollographql.apollo3.api.FakeResolver
-import com.apollographql.apollo3.api.buildFragmentData
 import com.apollographql.apollo3.compiler.applyIf
 import com.apollographql.apollo3.compiler.codegen.Identifier
 import com.apollographql.apollo3.compiler.codegen.Identifier.block
@@ -23,7 +19,6 @@ import com.apollographql.apollo3.compiler.codegen.maybeFlatten
 import com.apollographql.apollo3.compiler.ir.IrCompositeType2
 import com.apollographql.apollo3.compiler.ir.IrFragmentDefinition
 import com.apollographql.apollo3.compiler.ir.IrNonNullType2
-import com.apollographql.apollo3.compiler.ir.IrType2
 import com.squareup.kotlinpoet.ClassName
 import com.squareup.kotlinpoet.CodeBlock
 import com.squareup.kotlinpoet.FunSpec
@@ -181,6 +176,7 @@ internal class FragmentBuilder(
                   .add("$block,\n")
                   .add("$resolver,\n")
                   .add("%T.$type,\n", context.resolver.resolveSchemaType(typename))
+                  .add("%T,\n", context.resolver.resolveCustomScalarAdapters())
                   .unindent()
                   .add(")\n")
                   .build()
diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/file/OperationBuilder.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/file/OperationBuilder.kt
index 84a0c296d8..43ce48b749 100644
--- a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/file/OperationBuilder.kt
+++ b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/codegen/kotlin/file/OperationBuilder.kt
@@ -216,7 +216,7 @@ internal class OperationBuilder(
       return FunSpec.builder(Identifier.Data)
           .addParameter(
               ParameterSpec.builder(
-                  Identifier.resolver,
+                  resolver,
                   KotlinSymbols.FakeResolver
               ).defaultValue(
                   CodeBlock.of("%T(%T.all)", KotlinSymbols.DefaultFakeResolver, context.resolver.resolveSchema())
@@ -244,7 +244,8 @@ internal class OperationBuilder(
                       KotlinMemberNames.GlobalBuilder,
                       context.resolver.resolveBuilderFun(typename),
                   )
-                  .add("${Identifier.resolver}\n")
+                  .add("$resolver,\n")
+                  .add("%T,\n", context.resolver.resolveCustomScalarAdapters())
                   .unindent()
                   .add(")\n")
                   .build()
diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/ir/IrBuilder.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/ir/IrBuilder.kt
index c5650c438b..5312a97bff 100644
--- a/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/ir/IrBuilder.kt
+++ b/apollo-compiler/src/main/kotlin/com/apollographql/apollo3/compiler/ir/IrBuilder.kt
@@ -74,6 +74,7 @@ internal class IrBuilder(
     private val codegenModels: String,
     private val generateOptionalOperationVariables: Boolean,
     private val generateDataBuilders: Boolean,
+    private val fieldsOnDisjointTypesMustMerge: Boolean,
 ) : FieldMerger {
   private val usedTypes = mutableListOf<String>()
 
@@ -454,7 +455,7 @@ internal class IrBuilder(
   private fun GQLFragmentDefinition.toIr(): IrFragmentDefinition {
     val typeDefinition = schema.typeDefinition(typeCondition.name)
 
-    val inferredVariables = inferVariables(schema, allFragmentDefinitions)
+    val inferredVariables = inferVariables(schema, allFragmentDefinitions, fieldsOnDisjointTypesMustMerge)
 
     val interfaceModelGroup = builder.buildFragmentInterface(
         fragmentName = name
@@ -770,7 +771,7 @@ internal fun List<GQLDirective>.toBooleanExpression(): BooleanExpression<BTerm>
 }
 
 internal fun GQLDirective.toDeferBooleanExpression(): BooleanExpression<BTerm>? {
-  if (name != "experimental_defer") return null
+  if (name != "defer") return null
   val ifArgumentValue = arguments?.arguments?.firstOrNull { it.name == "if" }?.value ?: GQLBooleanValue(value = true)
 
   val labelArgumentValue = arguments?.arguments?.firstOrNull { it.name == "label" }?.value
diff --git a/apollo-gradle-plugin-external/api/apollo-gradle-plugin-external.api b/apollo-gradle-plugin-external/api/apollo-gradle-plugin-external.api
index a3af587a94..1a1435be48 100644
--- a/apollo-gradle-plugin-external/api/apollo-gradle-plugin-external.api
+++ b/apollo-gradle-plugin-external/api/apollo-gradle-plugin-external.api
@@ -69,6 +69,7 @@ public abstract interface class com/apollographql/apollo3/gradle/api/Service {
 	public abstract fun getDebugDir ()Lorg/gradle/api/file/DirectoryProperty;
 	public abstract fun getExcludes ()Lorg/gradle/api/provider/ListProperty;
 	public abstract fun getFailOnWarnings ()Lorg/gradle/api/provider/Property;
+	public abstract fun getFieldsOnDisjointTypesMustMerge ()Lorg/gradle/api/provider/Property;
 	public abstract fun getFlattenModels ()Lorg/gradle/api/provider/Property;
 	public abstract fun getGenerateApolloMetadata ()Lorg/gradle/api/provider/Property;
 	public abstract fun getGenerateAsInternal ()Lorg/gradle/api/provider/Property;
diff --git a/apollo-gradle-plugin-external/src/main/kotlin/com/apollographql/apollo3/gradle/api/Service.kt b/apollo-gradle-plugin-external/src/main/kotlin/com/apollographql/apollo3/gradle/api/Service.kt
index c8fa768cfd..4304b95be8 100644
--- a/apollo-gradle-plugin-external/src/main/kotlin/com/apollographql/apollo3/gradle/api/Service.kt
+++ b/apollo-gradle-plugin-external/src/main/kotlin/com/apollographql/apollo3/gradle/api/Service.kt
@@ -542,6 +542,18 @@ interface Service {
   @ApolloExperimental
   val requiresOptInAnnotation: Property<String>
 
+  /**
+   * Whether fields with different shape are disallowed to be merged in disjoint types.
+   *
+   * Note: setting this to `false` relaxes the standard GraphQL [FieldsInSetCanMerge](https://spec.graphql.org/draft/#FieldsInSetCanMerge()) validation which may still be
+   * run on the backend.
+   *
+   * See also [issue 4320](https://github.com/apollographql/apollo-kotlin/issues/4320)
+   *
+   * Default: true.
+   */
+  val fieldsOnDisjointTypesMustMerge: Property<Boolean>
+
   /**
    * A shorthand method that configures defaults that match Apollo Android 2.x codegen
    *
diff --git a/apollo-gradle-plugin-external/src/main/kotlin/com/apollographql/apollo3/gradle/internal/ApolloGenerateSourcesTask.kt b/apollo-gradle-plugin-external/src/main/kotlin/com/apollographql/apollo3/gradle/internal/ApolloGenerateSourcesTask.kt
index eeda6b95b3..aea41db05e 100644
--- a/apollo-gradle-plugin-external/src/main/kotlin/com/apollographql/apollo3/gradle/internal/ApolloGenerateSourcesTask.kt
+++ b/apollo-gradle-plugin-external/src/main/kotlin/com/apollographql/apollo3/gradle/internal/ApolloGenerateSourcesTask.kt
@@ -16,6 +16,7 @@ import com.apollographql.apollo3.compiler.Options.Companion.defaultAlwaysGenerat
 import com.apollographql.apollo3.compiler.Options.Companion.defaultCodegenModels
 import com.apollographql.apollo3.compiler.Options.Companion.defaultRequiresOptInAnnotation
 import com.apollographql.apollo3.compiler.Options.Companion.defaultFailOnWarnings
+import com.apollographql.apollo3.compiler.Options.Companion.defaultFieldsOnDisjointTypesMustMerge
 import com.apollographql.apollo3.compiler.Options.Companion.defaultGenerateAsInternal
 import com.apollographql.apollo3.compiler.Options.Companion.defaultGenerateDataBuilders
 import com.apollographql.apollo3.compiler.Options.Companion.defaultGenerateFilterNotNull
@@ -209,6 +210,10 @@ abstract class ApolloGenerateSourcesTask : DefaultTask() {
   @get:Optional
   abstract val requiresOptInAnnotation: Property<String>
 
+  @get:Input
+  @get:Optional
+  abstract val fieldsOnDisjointTypesMustMerge: Property<Boolean>
+
   @TaskAction
   fun taskAction() {
     val metadata = metadataFiles.files.toList().map { ApolloMetadata.readFrom(it) }
@@ -309,6 +314,7 @@ abstract class ApolloGenerateSourcesTask : DefaultTask() {
         generateOptionalOperationVariables = generateOptionalOperationVariables.getOrElse(defaultGenerateOptionalOperationVariables),
         addJvmOverloads = addJvmOverloads.getOrElse(defaultAddJvmOverloads),
         requiresOptInAnnotation = requiresOptInAnnotation.getOrElse(defaultRequiresOptInAnnotation),
+        fieldsOnDisjointTypesMustMerge = fieldsOnDisjointTypesMustMerge.getOrElse(defaultFieldsOnDisjointTypesMustMerge)
     )
 
     val outputCompilerMetadata = ApolloCompiler.write(options)
diff --git a/apollo-gradle-plugin-external/src/main/kotlin/com/apollographql/apollo3/gradle/internal/DefaultApolloExtension.kt b/apollo-gradle-plugin-external/src/main/kotlin/com/apollographql/apollo3/gradle/internal/DefaultApolloExtension.kt
index c7cb351071..fe872730d8 100644
--- a/apollo-gradle-plugin-external/src/main/kotlin/com/apollographql/apollo3/gradle/internal/DefaultApolloExtension.kt
+++ b/apollo-gradle-plugin-external/src/main/kotlin/com/apollographql/apollo3/gradle/internal/DefaultApolloExtension.kt
@@ -572,6 +572,7 @@ abstract class DefaultApolloExtension(
       task.generateOptionalOperationVariables.set(service.generateOptionalOperationVariables)
       task.languageVersion.set(service.languageVersion)
       task.requiresOptInAnnotation.set(service.requiresOptInAnnotation)
+      task.fieldsOnDisjointTypesMustMerge.set(service.fieldsOnDisjointTypesMustMerge)
     }
   }
 
diff --git a/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo3/internal/DeferredJsonMerger.kt b/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo3/internal/DeferredJsonMerger.kt
index a62741aae2..83acb0f79e 100644
--- a/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo3/internal/DeferredJsonMerger.kt
+++ b/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo3/internal/DeferredJsonMerger.kt
@@ -6,6 +6,9 @@ import com.apollographql.apollo3.api.json.BufferedSourceJsonReader
 import com.apollographql.apollo3.api.json.readAny
 import okio.BufferedSource
 
+private typealias JsonMap = Map<String, Any?>
+private typealias MutableJsonMap = MutableMap<String, Any?>
+
 /**
  * Utility class for merging GraphQL JSON payloads received in multiple chunks when using the `@defer` directive.
  *
@@ -15,12 +18,15 @@ import okio.BufferedSource
  * The fields in `data` are merged into the node found in [merged] at `path` (for the first call to [merge], the payload is
  * copied to [merged] as-is).
  *
- * `errors` and `extensions` fields are not merged: they are copied as-is (if present) to the [merged] Map at each call to [merge].
+ * `errors` in incremental items (if present) are merged together in an array and then set to the `errors` field of the [merged] Map,
+ * at each call to [merge].
+ * `extensions` in incremental items (if present) are merged together in an array and then set to the `extensions/incremental` field of the
+ * [merged] Map, at each call to [merge].
  */
 @ApolloInternal
 class DeferredJsonMerger {
-  private val _merged = mutableMapOf<String, Any?>()
-  val merged: Map<String, Any?> = _merged
+  private val _merged: MutableJsonMap = mutableMapOf()
+  val merged: JsonMap = _merged
 
   private val _mergedFragmentIds = mutableSetOf<DeferredFragmentIdentifier>()
   val mergedFragmentIds: Set<DeferredFragmentIdentifier> = _mergedFragmentIds
@@ -28,56 +34,69 @@ class DeferredJsonMerger {
   var hasNext: Boolean = true
     private set
 
-  fun merge(payload: BufferedSource): Map<String, Any?> {
+  fun merge(payload: BufferedSource): JsonMap {
     val payloadMap = jsonToMap(payload)
     return merge(payloadMap)
   }
 
-  fun merge(payload: Map<String, Any?>): Map<String, Any?> {
+  @Suppress("UNCHECKED_CAST")
+  fun merge(payload: JsonMap): JsonMap {
     if (merged.isEmpty()) {
       // Initial payload, no merging needed
       _merged += payload
       return merged
     }
 
-    mergeData(payload)
-    if (payload.containsKey("errors")) {
-      _merged["errors"] = payload["errors"]
-    } else {
-      _merged.remove("errors")
-    }
-    if (payload.containsKey("extensions")) {
-      _merged["extensions"] = payload["extensions"]
-    } else {
-      _merged.remove("extensions")
+    val incrementalList = payload["incremental"] as? List<JsonMap>
+    if (incrementalList != null) {
+      val mergedErrors = mutableListOf<JsonMap>()
+      val mergedExtensions = mutableListOf<JsonMap>()
+      for (incrementalItem in incrementalList) {
+        mergeData(incrementalItem)
+        // Merge errors and extensions (if any) of the incremental list
+        (incrementalItem["errors"] as? List<JsonMap>)?.let { mergedErrors += it }
+        (incrementalItem["extensions"] as? JsonMap)?.let { mergedExtensions += it }
+      }
+      // Keep only this payload's errors and extensions, if any
+      if (mergedErrors.isNotEmpty()) {
+        _merged["errors"] = mergedErrors
+      } else {
+        _merged.remove("errors")
+      }
+      if (mergedExtensions.isNotEmpty()) {
+        _merged["extensions"] = mapOf("incremental" to mergedExtensions)
+      } else {
+        _merged.remove("extensions")
+      }
     }
 
+    hasNext = payload["hasNext"] as Boolean? ?: false
+
     return merged
   }
 
   @Suppress("UNCHECKED_CAST")
-  private fun mergeData(payloadMap: Map<String, Any?>) {
-    val payloadData = payloadMap["data"] as Map<String, Any?>?
-    val payloadPath = payloadMap["path"] as List<Any>
-    val mergedData = merged["data"] as Map<String, Any?>
-    hasNext = payloadMap["hasNext"] as Boolean? ?: false
+  private fun mergeData(incrementalItem: JsonMap) {
+    val data = incrementalItem["data"] as JsonMap?
+    val path = incrementalItem["path"] as List<Any>
+    val mergedData = merged["data"] as JsonMap
 
     // payloadData can be null if there are errors
-    if (payloadData != null) {
-      val nodeToMergeInto = nodeAtPath(mergedData, payloadPath) as MutableMap<String, Any?>
-      deepMerge(nodeToMergeInto, payloadData)
+    if (data != null) {
+      val nodeToMergeInto = nodeAtPath(mergedData, path) as MutableJsonMap
+      deepMerge(nodeToMergeInto, data)
 
-      _mergedFragmentIds += DeferredFragmentIdentifier(path = payloadPath, label = payloadMap["label"] as String?)
+      _mergedFragmentIds += DeferredFragmentIdentifier(path = path, label = incrementalItem["label"] as String?)
     }
   }
 
   @Suppress("UNCHECKED_CAST")
-  private fun deepMerge(destination: MutableMap<String, Any?>, map: Map<String, Any?>) {
+  private fun deepMerge(destination: MutableJsonMap, map: JsonMap) {
     for ((key, value) in map) {
       if (destination.containsKey(key) && destination[key] is MutableMap<*, *>) {
         // Objects: merge recursively
-        val fieldDestination = destination[key] as MutableMap<String, Any?>
-        val fieldMap = value as? Map<String, Any?> ?: error("'$key' is an object in destination but not in map")
+        val fieldDestination = destination[key] as MutableJsonMap
+        val fieldMap = value as? JsonMap ?: error("'$key' is an object in destination but not in map")
         deepMerge(destination = fieldDestination, map = fieldMap)
       } else {
         // Other types: add / overwrite
@@ -87,21 +106,21 @@ class DeferredJsonMerger {
   }
 
   @Suppress("UNCHECKED_CAST")
-  private fun jsonToMap(json: BufferedSource): Map<String, Any?> = BufferedSourceJsonReader(json).readAny() as Map<String, Any?>
+  private fun jsonToMap(json: BufferedSource): JsonMap = BufferedSourceJsonReader(json).readAny() as JsonMap
 
 
   /**
    * Find the node in the [map] at the given [path].
    * @param path The path to the node to find, as a list of either `String` (name of field in object) or `Int` (index of element in array).
    */
-  private fun nodeAtPath(map: Map<String, Any?>, path: List<Any>): Any? {
+  private fun nodeAtPath(map: JsonMap, path: List<Any>): Any? {
     var node: Any? = map
     for (key in path) {
       node = if (node is List<*>) {
         node[key as Int]
       } else {
         @Suppress("UNCHECKED_CAST")
-        node as Map<String, Any?>
+        node as JsonMap
         node[key]
       }
     }
@@ -115,6 +134,6 @@ class DeferredJsonMerger {
   }
 }
 
-internal fun Map<String, Any?>.isDeferred(): Boolean {
+internal fun JsonMap.isDeferred(): Boolean {
   return keys.contains("hasNext")
 }
diff --git a/docs/source/tutorial/08-add-a-details-view.mdx b/docs/source/tutorial/08-add-a-details-view.mdx
index 786c9274b2..4882439ee5 100644
--- a/docs/source/tutorial/08-add-a-details-view.mdx
+++ b/docs/source/tutorial/08-add-a-details-view.mdx
@@ -9,6 +9,10 @@ In this section, you'll write a second GraphQL query that requests details about
 In `LaunchListAdapter.kt`, add a click listener:
 
 ```kotlin title="app/src/main/java/com/example/rocketreserver/LaunchListAdapter.kt"
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+        val binding = LaunchItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+        return ViewHolder(binding)
+    }
     var onEndOfListReached: (() -> Unit)? = null
     var onItemClicked: ((LaunchListQuery.Launch) -> Unit)? = null
 


Test patch:
diff --git a/apollo-compiler/src/test/graphql/com/example/__schema/java/operationBased/__schema/type/__Schema.java.expected b/apollo-compiler/src/test/graphql/com/example/__schema/java/operationBased/__schema/type/__Schema.java.expected
index 9d907d39cc..8c6d9bb979 100644
--- a/apollo-compiler/src/test/graphql/com/example/__schema/java/operationBased/__schema/type/__Schema.java.expected
+++ b/apollo-compiler/src/test/graphql/com/example/__schema/java/operationBased/__schema/type/__Schema.java.expected
@@ -6,6 +6,7 @@
 package com.example.__schema.type;
 
 import com.apollographql.apollo3.api.CompiledNamedType;
+import com.apollographql.apollo3.api.CustomScalarAdapters;
 import com.apollographql.apollo3.api.ObjectType;
 import com.apollographql.apollo3.api.PossibleTypes;
 import java.util.Arrays;
@@ -15,6 +16,9 @@ import java.util.List;
  * A Schema object containing all the composite types and a possibleTypes helper function
  */
 public class __Schema {
+  public static CustomScalarAdapters customScalarAdapters = new CustomScalarAdapters.Builder()
+    .build();
+
   public static List<CompiledNamedType> types = Arrays.asList(
     Bar.type,
     BarObject.type,
diff --git a/apollo-compiler/src/test/graphql/com/example/__schema/kotlin/responseBased/__schema/type/__CustomScalarAdapters.kt.expected b/apollo-compiler/src/test/graphql/com/example/__schema/kotlin/responseBased/__schema/type/__CustomScalarAdapters.kt.expected
new file mode 100644
index 0000000000..0e601e261a
--- /dev/null
+++ b/apollo-compiler/src/test/graphql/com/example/__schema/kotlin/responseBased/__schema/type/__CustomScalarAdapters.kt.expected
@@ -0,0 +1,15 @@
+//
+// AUTO-GENERATED FILE. DO NOT MODIFY.
+//
+// This class was automatically generated by Apollo GraphQL version '$VERSION'.
+//
+package com.example.__schema.type
+
+import com.apollographql.apollo3.api.CustomScalarAdapters
+
+/**
+ * A [CustomScalarAdapters] instance containing all the adapters known at build time
+ */
+public val __CustomScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Builder()
+      .build()
+
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/TestOperation.graphql b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/TestOperation.graphql
index 957e46a705..70da48d399 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/TestOperation.graphql
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/TestOperation.graphql
@@ -1,13 +1,13 @@
 query SpreadSingleWithDefer {
   hero {
-    ...DroidDetails @experimental_defer
+    ...DroidDetails @defer
   }
 }
 
 query SpreadMultipleWithDefer {
   hero {
-    ...DroidDetails @experimental_defer(label: "a_first")
-    ...DroidDetails @experimental_defer(label: "a_second")
+    ...DroidDetails @defer(label: "a_first")
+    ...DroidDetails @defer(label: "a_second")
   }
 }
 
@@ -15,10 +15,10 @@ query SpreadMultipleWithDeferAndInclude($cond1: Boolean!, $cond2: Boolean!, $con
   hero {
     ...DroidDetails @include(if: $cond1)
     ...DroidDetails @skip(if: $cond2)
-    ...DroidDetails @experimental_defer(label: "b_first")
-    ...DroidDetails @experimental_defer(label: "b_second")
-    ...DroidDetails @experimental_defer(label: "b_third") @include(if: $cond3)
-    ...DroidDetails @experimental_defer(label: "b_fourth") @skip(if: $cond4)
+    ...DroidDetails @defer(label: "b_first")
+    ...DroidDetails @defer(label: "b_second")
+    ...DroidDetails @defer(label: "b_third") @include(if: $cond3)
+    ...DroidDetails @defer(label: "b_fourth") @skip(if: $cond4)
   }
 }
 
@@ -28,9 +28,9 @@ fragment DroidDetails on Droid {
 
 query SpreadMultipleWithDeferWithIf($cond: Boolean!) {
   hero {
-    ...CharacterDetails @experimental_defer(label: "c_first" if: false)
-    ...CharacterDetails2 @experimental_defer(label: "c_second" if: true)
-    ...CharacterDetails3 @experimental_defer(label: "c_third" if: $cond)
+    ...CharacterDetails @defer(label: "c_first" if: false)
+    ...CharacterDetails2 @defer(label: "c_second" if: true)
+    ...CharacterDetails3 @defer(label: "c_third" if: $cond)
   }
 }
 
@@ -48,7 +48,7 @@ fragment CharacterDetails3 on Droid {
 
 query InlineSingleWithDefer {
   hero {
-    ... on Droid @experimental_defer {
+    ... on Droid @defer {
       name
     }
   }
@@ -56,11 +56,11 @@ query InlineSingleWithDefer {
 
 query InlineMultipleWithDefer {
   hero {
-    ... on Droid @experimental_defer(label: "d_first") {
+    ... on Droid @defer(label: "d_first") {
       name
     }
 
-    ... on Droid @experimental_defer(label: "d_second") {
+    ... on Droid @defer(label: "d_second") {
       id
     }
   }
@@ -74,16 +74,16 @@ query InlineMultipleWithDeferAndInclude($cond1: Boolean!, $cond2: Boolean!, $con
     ... on Droid @skip(if: $cond2) {
       id
     }
-    ... on Droid @experimental_defer(label: "e_first") {
+    ... on Droid @defer(label: "e_first") {
       name
     }
-    ... on Droid @experimental_defer(label: "e_second") {
+    ... on Droid @defer(label: "e_second") {
       id
     }
-    ... on Droid @experimental_defer(label: "e_third") @include(if: $cond3) {
+    ... on Droid @defer(label: "e_third") @include(if: $cond3) {
       name
     }
-    ... on Droid @experimental_defer(label: "e_fourth") @skip(if: $cond4) {
+    ... on Droid @defer(label: "e_fourth") @skip(if: $cond4) {
       id
     }
   }
@@ -91,15 +91,15 @@ query InlineMultipleWithDeferAndInclude($cond1: Boolean!, $cond2: Boolean!, $con
 
 query InlineMultipleWithDeferWithIf($cond: Boolean!) {
   hero {
-    ... on Character @experimental_defer(label: "f_first" if: false) {
+    ... on Character @defer(label: "f_first" if: false) {
       id
     }
 
-    ... on Character @experimental_defer(label: "f_second" if: true) {
+    ... on Character @defer(label: "f_second" if: true) {
       name
     }
 
-    ... on Character @experimental_defer(label: "f_third" if: $cond) {
+    ... on Character @defer(label: "f_third" if: $cond) {
       name
     }
   }
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDefer.java.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDefer.java.expected
index b5148d72e2..7aaae05a22 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDefer.java.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDefer.java.expected
@@ -19,7 +19,7 @@ import java.lang.Override;
 import java.lang.String;
 
 public class InlineMultipleWithDefer implements Query<InlineMultipleWithDefer.Data> {
-  public static final String OPERATION_ID = "9811408b41402ad913abcd7bb5ce221d926a3c545f3f401706b25aefa816081e";
+  public static final String OPERATION_ID = "9331f0c719bafb010d29f76c720d4488f45c5196bf3fb8908b2be30bc546e355";
 
   /**
    * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -28,16 +28,16 @@ public class InlineMultipleWithDefer implements Query<InlineMultipleWithDefer.Da
    * query InlineMultipleWithDefer {
    *   hero {
    *     __typename
-   *     ... on Droid @experimental_defer(label: "d_first") {
+   *     ... on Droid @defer(label: "d_first") {
    *       name
    *     }
-   *     ... on Droid @experimental_defer(label: "d_second") {
+   *     ... on Droid @defer(label: "d_second") {
    *       id
    *     }
    *   }
    * }
    */
-  public static final String OPERATION_DOCUMENT = "query InlineMultipleWithDefer { hero { __typename ... on Droid @experimental_defer(label: \"d_first\") { name } ... on Droid @experimental_defer(label: \"d_second\") { id } } }";
+  public static final String OPERATION_DOCUMENT = "query InlineMultipleWithDefer { hero { __typename ... on Droid @defer(label: \"d_first\") { name } ... on Droid @defer(label: \"d_second\") { id } } }";
 
   public static final String OPERATION_NAME = "InlineMultipleWithDefer";
 
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDeferAndInclude.java.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDeferAndInclude.java.expected
index 8bb3680a35..deca3a7a67 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDeferAndInclude.java.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDeferAndInclude.java.expected
@@ -21,7 +21,7 @@ import java.lang.Override;
 import java.lang.String;
 
 public class InlineMultipleWithDeferAndInclude implements Query<InlineMultipleWithDeferAndInclude.Data> {
-  public static final String OPERATION_ID = "90fbc93ce8710e768303863f05c634101ecab0723c10e370c3b8c643ff30f96e";
+  public static final String OPERATION_ID = "f159c1ac4f33a49c5c9833e06a2606e2fc7ede812e119909eb45f95dc2aa01fc";
 
   /**
    * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -36,22 +36,22 @@ public class InlineMultipleWithDeferAndInclude implements Query<InlineMultipleWi
    *     ... on Droid @skip(if: $cond2) {
    *       id
    *     }
-   *     ... on Droid @experimental_defer(label: "e_first") {
+   *     ... on Droid @defer(label: "e_first") {
    *       name
    *     }
-   *     ... on Droid @experimental_defer(label: "e_second") {
+   *     ... on Droid @defer(label: "e_second") {
    *       id
    *     }
-   *     ... on Droid @experimental_defer(label: "e_third") @include(if: $cond3) {
+   *     ... on Droid @defer(label: "e_third") @include(if: $cond3) {
    *       name
    *     }
-   *     ... on Droid @experimental_defer(label: "e_fourth") @skip(if: $cond4) {
+   *     ... on Droid @defer(label: "e_fourth") @skip(if: $cond4) {
    *       id
    *     }
    *   }
    * }
    */
-  public static final String OPERATION_DOCUMENT = "query InlineMultipleWithDeferAndInclude($cond1: Boolean!, $cond2: Boolean!, $cond3: Boolean!, $cond4: Boolean!) { hero { __typename ... on Droid @include(if: $cond1) { name } ... on Droid @skip(if: $cond2) { id } ... on Droid @experimental_defer(label: \"e_first\") { name } ... on Droid @experimental_defer(label: \"e_second\") { id } ... on Droid @experimental_defer(label: \"e_third\") @include(if: $cond3) { name } ... on Droid @experimental_defer(label: \"e_fourth\") @skip(if: $cond4) { id } } }";
+  public static final String OPERATION_DOCUMENT = "query InlineMultipleWithDeferAndInclude($cond1: Boolean!, $cond2: Boolean!, $cond3: Boolean!, $cond4: Boolean!) { hero { __typename ... on Droid @include(if: $cond1) { name } ... on Droid @skip(if: $cond2) { id } ... on Droid @defer(label: \"e_first\") { name } ... on Droid @defer(label: \"e_second\") { id } ... on Droid @defer(label: \"e_third\") @include(if: $cond3) { name } ... on Droid @defer(label: \"e_fourth\") @skip(if: $cond4) { id } } }";
 
   public static final String OPERATION_NAME = "InlineMultipleWithDeferAndInclude";
 
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDeferWithIf.java.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDeferWithIf.java.expected
index 2ca5b7a5f5..76748030c9 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDeferWithIf.java.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDeferWithIf.java.expected
@@ -21,7 +21,7 @@ import java.lang.Override;
 import java.lang.String;
 
 public class InlineMultipleWithDeferWithIf implements Query<InlineMultipleWithDeferWithIf.Data> {
-  public static final String OPERATION_ID = "3e777ab8534ca2847df43e92a828787127e9536d7c1ab8136985620443c2b991";
+  public static final String OPERATION_ID = "9dc79c78df7375c3d860d16df3ea839ea45c7f8420976f63414610d338f0caf1";
 
   /**
    * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -30,19 +30,19 @@ public class InlineMultipleWithDeferWithIf implements Query<InlineMultipleWithDe
    * query InlineMultipleWithDeferWithIf($cond: Boolean!) {
    *   hero {
    *     __typename
-   *     ... on Character @experimental_defer(label: "f_first", if: false) {
+   *     ... on Character @defer(label: "f_first", if: false) {
    *       id
    *     }
-   *     ... on Character @experimental_defer(label: "f_second", if: true) {
+   *     ... on Character @defer(label: "f_second", if: true) {
    *       name
    *     }
-   *     ... on Character @experimental_defer(label: "f_third", if: $cond) {
+   *     ... on Character @defer(label: "f_third", if: $cond) {
    *       name
    *     }
    *   }
    * }
    */
-  public static final String OPERATION_DOCUMENT = "query InlineMultipleWithDeferWithIf($cond: Boolean!) { hero { __typename ... on Character @experimental_defer(label: \"f_first\", if: false) { id } ... on Character @experimental_defer(label: \"f_second\", if: true) { name } ... on Character @experimental_defer(label: \"f_third\", if: $cond) { name } } }";
+  public static final String OPERATION_DOCUMENT = "query InlineMultipleWithDeferWithIf($cond: Boolean!) { hero { __typename ... on Character @defer(label: \"f_first\", if: false) { id } ... on Character @defer(label: \"f_second\", if: true) { name } ... on Character @defer(label: \"f_third\", if: $cond) { name } } }";
 
   public static final String OPERATION_NAME = "InlineMultipleWithDeferWithIf";
 
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/InlineSingleWithDefer.java.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/InlineSingleWithDefer.java.expected
index 44a87c6d5a..59ebfd4837 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/InlineSingleWithDefer.java.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/InlineSingleWithDefer.java.expected
@@ -19,7 +19,7 @@ import java.lang.Override;
 import java.lang.String;
 
 public class InlineSingleWithDefer implements Query<InlineSingleWithDefer.Data> {
-  public static final String OPERATION_ID = "da1ea6b7b54f4bc6e0754b9bbe45a002500fef00de7f3605a4efd02133c20def";
+  public static final String OPERATION_ID = "70222802c9192f810f19a54d31e039ce0fc3b8baac231841f0a0e8b4200c312b";
 
   /**
    * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -28,13 +28,13 @@ public class InlineSingleWithDefer implements Query<InlineSingleWithDefer.Data>
    * query InlineSingleWithDefer {
    *   hero {
    *     __typename
-   *     ... on Droid @experimental_defer {
+   *     ... on Droid @defer {
    *       name
    *     }
    *   }
    * }
    */
-  public static final String OPERATION_DOCUMENT = "query InlineSingleWithDefer { hero { __typename ... on Droid @experimental_defer { name } } }";
+  public static final String OPERATION_DOCUMENT = "query InlineSingleWithDefer { hero { __typename ... on Droid @defer { name } } }";
 
   public static final String OPERATION_NAME = "InlineSingleWithDefer";
 
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDefer.java.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDefer.java.expected
index 66d7236488..efa69b0ab6 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDefer.java.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDefer.java.expected
@@ -20,7 +20,7 @@ import java.lang.Override;
 import java.lang.String;
 
 public class SpreadMultipleWithDefer implements Query<SpreadMultipleWithDefer.Data> {
-  public static final String OPERATION_ID = "e52d7b5c6a99f54c7d8e7f1d6c4f6e604586b4c83b212436661068e98f4f7ad1";
+  public static final String OPERATION_ID = "7cd4d1b6a14f96cff4bbbdbd3b3ab1aa365ffc65e1515ff6e22935010543ad71";
 
   /**
    * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -29,8 +29,8 @@ public class SpreadMultipleWithDefer implements Query<SpreadMultipleWithDefer.Da
    * query SpreadMultipleWithDefer {
    *   hero {
    *     __typename
-   *     ...DroidDetails @experimental_defer(label: "a_first")
-   *     ...DroidDetails @experimental_defer(label: "a_second")
+   *     ...DroidDetails @defer(label: "a_first")
+   *     ...DroidDetails @defer(label: "a_second")
    *   }
    * }
    *
@@ -38,7 +38,7 @@ public class SpreadMultipleWithDefer implements Query<SpreadMultipleWithDefer.Da
    *   name
    * }
    */
-  public static final String OPERATION_DOCUMENT = "query SpreadMultipleWithDefer { hero { __typename ...DroidDetails @experimental_defer(label: \"a_first\") ...DroidDetails @experimental_defer(label: \"a_second\") } }  fragment DroidDetails on Droid { name }";
+  public static final String OPERATION_DOCUMENT = "query SpreadMultipleWithDefer { hero { __typename ...DroidDetails @defer(label: \"a_first\") ...DroidDetails @defer(label: \"a_second\") } }  fragment DroidDetails on Droid { name }";
 
   public static final String OPERATION_NAME = "SpreadMultipleWithDefer";
 
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferAndInclude.java.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferAndInclude.java.expected
index 3f6a1332e1..5c49d67940 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferAndInclude.java.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferAndInclude.java.expected
@@ -22,7 +22,7 @@ import java.lang.Override;
 import java.lang.String;
 
 public class SpreadMultipleWithDeferAndInclude implements Query<SpreadMultipleWithDeferAndInclude.Data> {
-  public static final String OPERATION_ID = "de31db86e7b05f57e3d3d700a4811e4aba6c56860dc18f94879165a5e04fcfe8";
+  public static final String OPERATION_ID = "1c9d61b4af65df2bfed92353244efbb66d20a35daf747507e66ed9d20dcd1517";
 
   /**
    * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -33,10 +33,10 @@ public class SpreadMultipleWithDeferAndInclude implements Query<SpreadMultipleWi
    *     __typename
    *     ...DroidDetails @include(if: $cond1)
    *     ...DroidDetails @skip(if: $cond2)
-   *     ...DroidDetails @experimental_defer(label: "b_first")
-   *     ...DroidDetails @experimental_defer(label: "b_second")
-   *     ...DroidDetails @experimental_defer(label: "b_third") @include(if: $cond3)
-   *     ...DroidDetails @experimental_defer(label: "b_fourth") @skip(if: $cond4)
+   *     ...DroidDetails @defer(label: "b_first")
+   *     ...DroidDetails @defer(label: "b_second")
+   *     ...DroidDetails @defer(label: "b_third") @include(if: $cond3)
+   *     ...DroidDetails @defer(label: "b_fourth") @skip(if: $cond4)
    *   }
    * }
    *
@@ -44,7 +44,7 @@ public class SpreadMultipleWithDeferAndInclude implements Query<SpreadMultipleWi
    *   name
    * }
    */
-  public static final String OPERATION_DOCUMENT = "query SpreadMultipleWithDeferAndInclude($cond1: Boolean!, $cond2: Boolean!, $cond3: Boolean!, $cond4: Boolean!) { hero { __typename ...DroidDetails @include(if: $cond1) ...DroidDetails @skip(if: $cond2) ...DroidDetails @experimental_defer(label: \"b_first\") ...DroidDetails @experimental_defer(label: \"b_second\") ...DroidDetails @experimental_defer(label: \"b_third\") @include(if: $cond3) ...DroidDetails @experimental_defer(label: \"b_fourth\") @skip(if: $cond4) } }  fragment DroidDetails on Droid { name }";
+  public static final String OPERATION_DOCUMENT = "query SpreadMultipleWithDeferAndInclude($cond1: Boolean!, $cond2: Boolean!, $cond3: Boolean!, $cond4: Boolean!) { hero { __typename ...DroidDetails @include(if: $cond1) ...DroidDetails @skip(if: $cond2) ...DroidDetails @defer(label: \"b_first\") ...DroidDetails @defer(label: \"b_second\") ...DroidDetails @defer(label: \"b_third\") @include(if: $cond3) ...DroidDetails @defer(label: \"b_fourth\") @skip(if: $cond4) } }  fragment DroidDetails on Droid { name }";
 
   public static final String OPERATION_NAME = "SpreadMultipleWithDeferAndInclude";
 
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferWithIf.java.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferWithIf.java.expected
index c586e04c49..c410e366e3 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferWithIf.java.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferWithIf.java.expected
@@ -24,7 +24,7 @@ import java.lang.Override;
 import java.lang.String;
 
 public class SpreadMultipleWithDeferWithIf implements Query<SpreadMultipleWithDeferWithIf.Data> {
-  public static final String OPERATION_ID = "f98db9747dd000723759253dacdd7a4e45eaa6003e459c001799901f96abbd8f";
+  public static final String OPERATION_ID = "ecdac17ca37d6cbf3a6e5059904fabfd96193cbe0720eb27880026b518feec9f";
 
   /**
    * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -33,9 +33,9 @@ public class SpreadMultipleWithDeferWithIf implements Query<SpreadMultipleWithDe
    * query SpreadMultipleWithDeferWithIf($cond: Boolean!) {
    *   hero {
    *     __typename
-   *     ...CharacterDetails @experimental_defer(label: "c_first", if: false)
-   *     ...CharacterDetails2 @experimental_defer(label: "c_second", if: true)
-   *     ...CharacterDetails3 @experimental_defer(label: "c_third", if: $cond)
+   *     ...CharacterDetails @defer(label: "c_first", if: false)
+   *     ...CharacterDetails2 @defer(label: "c_second", if: true)
+   *     ...CharacterDetails3 @defer(label: "c_third", if: $cond)
    *   }
    * }
    *
@@ -51,7 +51,7 @@ public class SpreadMultipleWithDeferWithIf implements Query<SpreadMultipleWithDe
    *   birthDate
    * }
    */
-  public static final String OPERATION_DOCUMENT = "query SpreadMultipleWithDeferWithIf($cond: Boolean!) { hero { __typename ...CharacterDetails @experimental_defer(label: \"c_first\", if: false) ...CharacterDetails2 @experimental_defer(label: \"c_second\", if: true) ...CharacterDetails3 @experimental_defer(label: \"c_third\", if: $cond) } }  fragment CharacterDetails on Droid { name }  fragment CharacterDetails2 on Droid { id }  fragment CharacterDetails3 on Droid { birthDate }";
+  public static final String OPERATION_DOCUMENT = "query SpreadMultipleWithDeferWithIf($cond: Boolean!) { hero { __typename ...CharacterDetails @defer(label: \"c_first\", if: false) ...CharacterDetails2 @defer(label: \"c_second\", if: true) ...CharacterDetails3 @defer(label: \"c_third\", if: $cond) } }  fragment CharacterDetails on Droid { name }  fragment CharacterDetails2 on Droid { id }  fragment CharacterDetails3 on Droid { birthDate }";
 
   public static final String OPERATION_NAME = "SpreadMultipleWithDeferWithIf";
 
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/SpreadSingleWithDefer.java.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/SpreadSingleWithDefer.java.expected
index 3ea706b416..eea1e28ee9 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/SpreadSingleWithDefer.java.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/java/operationBased/fragments_with_defer_and_include_directives/SpreadSingleWithDefer.java.expected
@@ -20,7 +20,7 @@ import java.lang.Override;
 import java.lang.String;
 
 public class SpreadSingleWithDefer implements Query<SpreadSingleWithDefer.Data> {
-  public static final String OPERATION_ID = "d7b739ea7224dce16c1992e5df95207a06f4f92b8231db42098755afac7a6439";
+  public static final String OPERATION_ID = "fad1c3622a6f35d5fa4ad7b5e4a8d50525c350d4688837022f0f7a6f0aed2ba0";
 
   /**
    * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -29,7 +29,7 @@ public class SpreadSingleWithDefer implements Query<SpreadSingleWithDefer.Data>
    * query SpreadSingleWithDefer {
    *   hero {
    *     __typename
-   *     ...DroidDetails @experimental_defer
+   *     ...DroidDetails @defer
    *   }
    * }
    *
@@ -37,7 +37,7 @@ public class SpreadSingleWithDefer implements Query<SpreadSingleWithDefer.Data>
    *   name
    * }
    */
-  public static final String OPERATION_DOCUMENT = "query SpreadSingleWithDefer { hero { __typename ...DroidDetails @experimental_defer } }  fragment DroidDetails on Droid { name }";
+  public static final String OPERATION_DOCUMENT = "query SpreadSingleWithDefer { hero { __typename ...DroidDetails @defer } }  fragment DroidDetails on Droid { name }";
 
   public static final String OPERATION_NAME = "SpreadSingleWithDefer";
 
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/InlineMultipleWithDefer.kt.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/InlineMultipleWithDefer.kt.expected
index b5ce7ac30a..c23f2e080d 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/InlineMultipleWithDefer.kt.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/InlineMultipleWithDefer.kt.expected
@@ -80,7 +80,7 @@ public class InlineMultipleWithDefer() : Query<InlineMultipleWithDefer.Data> {
 
   public companion object {
     public const val OPERATION_ID: String =
-        "9811408b41402ad913abcd7bb5ce221d926a3c545f3f401706b25aefa816081e"
+        "9331f0c719bafb010d29f76c720d4488f45c5196bf3fb8908b2be30bc546e355"
 
     /**
      * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -89,10 +89,10 @@ public class InlineMultipleWithDefer() : Query<InlineMultipleWithDefer.Data> {
      * query InlineMultipleWithDefer {
      *   hero {
      *     __typename
-     *     ... on Droid @experimental_defer(label: "d_first") {
+     *     ... on Droid @defer(label: "d_first") {
      *       name
      *     }
-     *     ... on Droid @experimental_defer(label: "d_second") {
+     *     ... on Droid @defer(label: "d_second") {
      *       id
      *     }
      *   }
@@ -100,7 +100,7 @@ public class InlineMultipleWithDefer() : Query<InlineMultipleWithDefer.Data> {
      */
     public val OPERATION_DOCUMENT: String
       get() =
-          "query InlineMultipleWithDefer { hero { __typename ... on Droid @experimental_defer(label: \"d_first\") { name } ... on Droid @experimental_defer(label: \"d_second\") { id } } }"
+          "query InlineMultipleWithDefer { hero { __typename ... on Droid @defer(label: \"d_first\") { name } ... on Droid @defer(label: \"d_second\") { id } } }"
 
     public const val OPERATION_NAME: String = "InlineMultipleWithDefer"
   }
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/InlineMultipleWithDeferAndInclude.kt.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/InlineMultipleWithDeferAndInclude.kt.expected
index 702207f155..807596fdd6 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/InlineMultipleWithDeferAndInclude.kt.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/InlineMultipleWithDeferAndInclude.kt.expected
@@ -129,7 +129,7 @@ public data class InlineMultipleWithDeferAndInclude(
 
   public companion object {
     public const val OPERATION_ID: String =
-        "90fbc93ce8710e768303863f05c634101ecab0723c10e370c3b8c643ff30f96e"
+        "f159c1ac4f33a49c5c9833e06a2606e2fc7ede812e119909eb45f95dc2aa01fc"
 
     /**
      * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -145,16 +145,16 @@ public data class InlineMultipleWithDeferAndInclude(
      *     ... on Droid @skip(if: $cond2) {
      *       id
      *     }
-     *     ... on Droid @experimental_defer(label: "e_first") {
+     *     ... on Droid @defer(label: "e_first") {
      *       name
      *     }
-     *     ... on Droid @experimental_defer(label: "e_second") {
+     *     ... on Droid @defer(label: "e_second") {
      *       id
      *     }
-     *     ... on Droid @experimental_defer(label: "e_third") @include(if: $cond3) {
+     *     ... on Droid @defer(label: "e_third") @include(if: $cond3) {
      *       name
      *     }
-     *     ... on Droid @experimental_defer(label: "e_fourth") @skip(if: $cond4) {
+     *     ... on Droid @defer(label: "e_fourth") @skip(if: $cond4) {
      *       id
      *     }
      *   }
@@ -162,7 +162,7 @@ public data class InlineMultipleWithDeferAndInclude(
      */
     public val OPERATION_DOCUMENT: String
       get() =
-          "query InlineMultipleWithDeferAndInclude(${'$'}cond1: Boolean!, ${'$'}cond2: Boolean!, ${'$'}cond3: Boolean!, ${'$'}cond4: Boolean!) { hero { __typename ... on Droid @include(if: ${'$'}cond1) { name } ... on Droid @skip(if: ${'$'}cond2) { id } ... on Droid @experimental_defer(label: \"e_first\") { name } ... on Droid @experimental_defer(label: \"e_second\") { id } ... on Droid @experimental_defer(label: \"e_third\") @include(if: ${'$'}cond3) { name } ... on Droid @experimental_defer(label: \"e_fourth\") @skip(if: ${'$'}cond4) { id } } }"
+          "query InlineMultipleWithDeferAndInclude(${'$'}cond1: Boolean!, ${'$'}cond2: Boolean!, ${'$'}cond3: Boolean!, ${'$'}cond4: Boolean!) { hero { __typename ... on Droid @include(if: ${'$'}cond1) { name } ... on Droid @skip(if: ${'$'}cond2) { id } ... on Droid @defer(label: \"e_first\") { name } ... on Droid @defer(label: \"e_second\") { id } ... on Droid @defer(label: \"e_third\") @include(if: ${'$'}cond3) { name } ... on Droid @defer(label: \"e_fourth\") @skip(if: ${'$'}cond4) { id } } }"
 
     public const val OPERATION_NAME: String = "InlineMultipleWithDeferAndInclude"
   }
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/InlineMultipleWithDeferWithIf.kt.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/InlineMultipleWithDeferWithIf.kt.expected
index 1b31cfb3cc..5d1f92f138 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/InlineMultipleWithDeferWithIf.kt.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/InlineMultipleWithDeferWithIf.kt.expected
@@ -90,7 +90,7 @@ public data class InlineMultipleWithDeferWithIf(
 
   public companion object {
     public const val OPERATION_ID: String =
-        "3e777ab8534ca2847df43e92a828787127e9536d7c1ab8136985620443c2b991"
+        "9dc79c78df7375c3d860d16df3ea839ea45c7f8420976f63414610d338f0caf1"
 
     /**
      * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -99,13 +99,13 @@ public data class InlineMultipleWithDeferWithIf(
      * query InlineMultipleWithDeferWithIf($cond: Boolean!) {
      *   hero {
      *     __typename
-     *     ... on Character @experimental_defer(label: "f_first", if: false) {
+     *     ... on Character @defer(label: "f_first", if: false) {
      *       id
      *     }
-     *     ... on Character @experimental_defer(label: "f_second", if: true) {
+     *     ... on Character @defer(label: "f_second", if: true) {
      *       name
      *     }
-     *     ... on Character @experimental_defer(label: "f_third", if: $cond) {
+     *     ... on Character @defer(label: "f_third", if: $cond) {
      *       name
      *     }
      *   }
@@ -113,7 +113,7 @@ public data class InlineMultipleWithDeferWithIf(
      */
     public val OPERATION_DOCUMENT: String
       get() =
-          "query InlineMultipleWithDeferWithIf(${'$'}cond: Boolean!) { hero { __typename ... on Character @experimental_defer(label: \"f_first\", if: false) { id } ... on Character @experimental_defer(label: \"f_second\", if: true) { name } ... on Character @experimental_defer(label: \"f_third\", if: ${'$'}cond) { name } } }"
+          "query InlineMultipleWithDeferWithIf(${'$'}cond: Boolean!) { hero { __typename ... on Character @defer(label: \"f_first\", if: false) { id } ... on Character @defer(label: \"f_second\", if: true) { name } ... on Character @defer(label: \"f_third\", if: ${'$'}cond) { name } } }"
 
     public const val OPERATION_NAME: String = "InlineMultipleWithDeferWithIf"
   }
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/InlineSingleWithDefer.kt.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/InlineSingleWithDefer.kt.expected
index 4e0966cec0..ee6cc409d8 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/InlineSingleWithDefer.kt.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/InlineSingleWithDefer.kt.expected
@@ -68,7 +68,7 @@ public class InlineSingleWithDefer() : Query<InlineSingleWithDefer.Data> {
 
   public companion object {
     public const val OPERATION_ID: String =
-        "da1ea6b7b54f4bc6e0754b9bbe45a002500fef00de7f3605a4efd02133c20def"
+        "70222802c9192f810f19a54d31e039ce0fc3b8baac231841f0a0e8b4200c312b"
 
     /**
      * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -77,15 +77,14 @@ public class InlineSingleWithDefer() : Query<InlineSingleWithDefer.Data> {
      * query InlineSingleWithDefer {
      *   hero {
      *     __typename
-     *     ... on Droid @experimental_defer {
+     *     ... on Droid @defer {
      *       name
      *     }
      *   }
      * }
      */
     public val OPERATION_DOCUMENT: String
-      get() =
-          "query InlineSingleWithDefer { hero { __typename ... on Droid @experimental_defer { name } } }"
+      get() = "query InlineSingleWithDefer { hero { __typename ... on Droid @defer { name } } }"
 
     public const val OPERATION_NAME: String = "InlineSingleWithDefer"
   }
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/SpreadMultipleWithDefer.kt.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/SpreadMultipleWithDefer.kt.expected
index 7bb3959f27..f0dd40c860 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/SpreadMultipleWithDefer.kt.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/SpreadMultipleWithDefer.kt.expected
@@ -68,7 +68,7 @@ public class SpreadMultipleWithDefer() : Query<SpreadMultipleWithDefer.Data> {
 
   public companion object {
     public const val OPERATION_ID: String =
-        "e52d7b5c6a99f54c7d8e7f1d6c4f6e604586b4c83b212436661068e98f4f7ad1"
+        "7cd4d1b6a14f96cff4bbbdbd3b3ab1aa365ffc65e1515ff6e22935010543ad71"
 
     /**
      * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -77,8 +77,8 @@ public class SpreadMultipleWithDefer() : Query<SpreadMultipleWithDefer.Data> {
      * query SpreadMultipleWithDefer {
      *   hero {
      *     __typename
-     *     ...DroidDetails @experimental_defer(label: "a_first")
-     *     ...DroidDetails @experimental_defer(label: "a_second")
+     *     ...DroidDetails @defer(label: "a_first")
+     *     ...DroidDetails @defer(label: "a_second")
      *   }
      * }
      *
@@ -88,7 +88,7 @@ public class SpreadMultipleWithDefer() : Query<SpreadMultipleWithDefer.Data> {
      */
     public val OPERATION_DOCUMENT: String
       get() =
-          "query SpreadMultipleWithDefer { hero { __typename ...DroidDetails @experimental_defer(label: \"a_first\") ...DroidDetails @experimental_defer(label: \"a_second\") } }  fragment DroidDetails on Droid { name }"
+          "query SpreadMultipleWithDefer { hero { __typename ...DroidDetails @defer(label: \"a_first\") ...DroidDetails @defer(label: \"a_second\") } }  fragment DroidDetails on Droid { name }"
 
     public const val OPERATION_NAME: String = "SpreadMultipleWithDefer"
   }
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferAndInclude.kt.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferAndInclude.kt.expected
index eb5458fe38..82b7e7013d 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferAndInclude.kt.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferAndInclude.kt.expected
@@ -69,7 +69,7 @@ public data class SpreadMultipleWithDeferAndInclude(
 
   public companion object {
     public const val OPERATION_ID: String =
-        "de31db86e7b05f57e3d3d700a4811e4aba6c56860dc18f94879165a5e04fcfe8"
+        "1c9d61b4af65df2bfed92353244efbb66d20a35daf747507e66ed9d20dcd1517"
 
     /**
      * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -81,10 +81,10 @@ public data class SpreadMultipleWithDeferAndInclude(
      *     __typename
      *     ...DroidDetails @include(if: $cond1)
      *     ...DroidDetails @skip(if: $cond2)
-     *     ...DroidDetails @experimental_defer(label: "b_first")
-     *     ...DroidDetails @experimental_defer(label: "b_second")
-     *     ...DroidDetails @experimental_defer(label: "b_third") @include(if: $cond3)
-     *     ...DroidDetails @experimental_defer(label: "b_fourth") @skip(if: $cond4)
+     *     ...DroidDetails @defer(label: "b_first")
+     *     ...DroidDetails @defer(label: "b_second")
+     *     ...DroidDetails @defer(label: "b_third") @include(if: $cond3)
+     *     ...DroidDetails @defer(label: "b_fourth") @skip(if: $cond4)
      *   }
      * }
      *
@@ -94,7 +94,7 @@ public data class SpreadMultipleWithDeferAndInclude(
      */
     public val OPERATION_DOCUMENT: String
       get() =
-          "query SpreadMultipleWithDeferAndInclude(${'$'}cond1: Boolean!, ${'$'}cond2: Boolean!, ${'$'}cond3: Boolean!, ${'$'}cond4: Boolean!) { hero { __typename ...DroidDetails @include(if: ${'$'}cond1) ...DroidDetails @skip(if: ${'$'}cond2) ...DroidDetails @experimental_defer(label: \"b_first\") ...DroidDetails @experimental_defer(label: \"b_second\") ...DroidDetails @experimental_defer(label: \"b_third\") @include(if: ${'$'}cond3) ...DroidDetails @experimental_defer(label: \"b_fourth\") @skip(if: ${'$'}cond4) } }  fragment DroidDetails on Droid { name }"
+          "query SpreadMultipleWithDeferAndInclude(${'$'}cond1: Boolean!, ${'$'}cond2: Boolean!, ${'$'}cond3: Boolean!, ${'$'}cond4: Boolean!) { hero { __typename ...DroidDetails @include(if: ${'$'}cond1) ...DroidDetails @skip(if: ${'$'}cond2) ...DroidDetails @defer(label: \"b_first\") ...DroidDetails @defer(label: \"b_second\") ...DroidDetails @defer(label: \"b_third\") @include(if: ${'$'}cond3) ...DroidDetails @defer(label: \"b_fourth\") @skip(if: ${'$'}cond4) } }  fragment DroidDetails on Droid { name }"
 
     public const val OPERATION_NAME: String = "SpreadMultipleWithDeferAndInclude"
   }
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferWithIf.kt.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferWithIf.kt.expected
index 4a6a975feb..f2c6518b68 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferWithIf.kt.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferWithIf.kt.expected
@@ -76,7 +76,7 @@ public data class SpreadMultipleWithDeferWithIf(
 
   public companion object {
     public const val OPERATION_ID: String =
-        "f98db9747dd000723759253dacdd7a4e45eaa6003e459c001799901f96abbd8f"
+        "ecdac17ca37d6cbf3a6e5059904fabfd96193cbe0720eb27880026b518feec9f"
 
     /**
      * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -85,9 +85,9 @@ public data class SpreadMultipleWithDeferWithIf(
      * query SpreadMultipleWithDeferWithIf($cond: Boolean!) {
      *   hero {
      *     __typename
-     *     ...CharacterDetails @experimental_defer(label: "c_first", if: false)
-     *     ...CharacterDetails2 @experimental_defer(label: "c_second", if: true)
-     *     ...CharacterDetails3 @experimental_defer(label: "c_third", if: $cond)
+     *     ...CharacterDetails @defer(label: "c_first", if: false)
+     *     ...CharacterDetails2 @defer(label: "c_second", if: true)
+     *     ...CharacterDetails3 @defer(label: "c_third", if: $cond)
      *   }
      * }
      *
@@ -105,7 +105,7 @@ public data class SpreadMultipleWithDeferWithIf(
      */
     public val OPERATION_DOCUMENT: String
       get() =
-          "query SpreadMultipleWithDeferWithIf(${'$'}cond: Boolean!) { hero { __typename ...CharacterDetails @experimental_defer(label: \"c_first\", if: false) ...CharacterDetails2 @experimental_defer(label: \"c_second\", if: true) ...CharacterDetails3 @experimental_defer(label: \"c_third\", if: ${'$'}cond) } }  fragment CharacterDetails on Droid { name }  fragment CharacterDetails2 on Droid { id }  fragment CharacterDetails3 on Droid { birthDate }"
+          "query SpreadMultipleWithDeferWithIf(${'$'}cond: Boolean!) { hero { __typename ...CharacterDetails @defer(label: \"c_first\", if: false) ...CharacterDetails2 @defer(label: \"c_second\", if: true) ...CharacterDetails3 @defer(label: \"c_third\", if: ${'$'}cond) } }  fragment CharacterDetails on Droid { name }  fragment CharacterDetails2 on Droid { id }  fragment CharacterDetails3 on Droid { birthDate }"
 
     public const val OPERATION_NAME: String = "SpreadMultipleWithDeferWithIf"
   }
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/SpreadSingleWithDefer.kt.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/SpreadSingleWithDefer.kt.expected
index f1fce89d17..ec94193440 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/SpreadSingleWithDefer.kt.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/compat/fragments_with_defer_and_include_directives/SpreadSingleWithDefer.kt.expected
@@ -68,7 +68,7 @@ public class SpreadSingleWithDefer() : Query<SpreadSingleWithDefer.Data> {
 
   public companion object {
     public const val OPERATION_ID: String =
-        "d7b739ea7224dce16c1992e5df95207a06f4f92b8231db42098755afac7a6439"
+        "fad1c3622a6f35d5fa4ad7b5e4a8d50525c350d4688837022f0f7a6f0aed2ba0"
 
     /**
      * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -77,7 +77,7 @@ public class SpreadSingleWithDefer() : Query<SpreadSingleWithDefer.Data> {
      * query SpreadSingleWithDefer {
      *   hero {
      *     __typename
-     *     ...DroidDetails @experimental_defer
+     *     ...DroidDetails @defer
      *   }
      * }
      *
@@ -87,7 +87,7 @@ public class SpreadSingleWithDefer() : Query<SpreadSingleWithDefer.Data> {
      */
     public val OPERATION_DOCUMENT: String
       get() =
-          "query SpreadSingleWithDefer { hero { __typename ...DroidDetails @experimental_defer } }  fragment DroidDetails on Droid { name }"
+          "query SpreadSingleWithDefer { hero { __typename ...DroidDetails @defer } }  fragment DroidDetails on Droid { name }"
 
     public const val OPERATION_NAME: String = "SpreadSingleWithDefer"
   }
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDefer.kt.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDefer.kt.expected
index c2e2ab7d64..d845c8beeb 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDefer.kt.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDefer.kt.expected
@@ -78,7 +78,7 @@ public class InlineMultipleWithDefer() : Query<InlineMultipleWithDefer.Data> {
 
   public companion object {
     public const val OPERATION_ID: String =
-        "9811408b41402ad913abcd7bb5ce221d926a3c545f3f401706b25aefa816081e"
+        "9331f0c719bafb010d29f76c720d4488f45c5196bf3fb8908b2be30bc546e355"
 
     /**
      * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -87,10 +87,10 @@ public class InlineMultipleWithDefer() : Query<InlineMultipleWithDefer.Data> {
      * query InlineMultipleWithDefer {
      *   hero {
      *     __typename
-     *     ... on Droid @experimental_defer(label: "d_first") {
+     *     ... on Droid @defer(label: "d_first") {
      *       name
      *     }
-     *     ... on Droid @experimental_defer(label: "d_second") {
+     *     ... on Droid @defer(label: "d_second") {
      *       id
      *     }
      *   }
@@ -98,7 +98,7 @@ public class InlineMultipleWithDefer() : Query<InlineMultipleWithDefer.Data> {
      */
     public val OPERATION_DOCUMENT: String
       get() =
-          "query InlineMultipleWithDefer { hero { __typename ... on Droid @experimental_defer(label: \"d_first\") { name } ... on Droid @experimental_defer(label: \"d_second\") { id } } }"
+          "query InlineMultipleWithDefer { hero { __typename ... on Droid @defer(label: \"d_first\") { name } ... on Droid @defer(label: \"d_second\") { id } } }"
 
     public const val OPERATION_NAME: String = "InlineMultipleWithDefer"
   }
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDeferAndInclude.kt.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDeferAndInclude.kt.expected
index 4ec8b3b3a5..3f57610116 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDeferAndInclude.kt.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDeferAndInclude.kt.expected
@@ -123,7 +123,7 @@ public data class InlineMultipleWithDeferAndInclude(
 
   public companion object {
     public const val OPERATION_ID: String =
-        "90fbc93ce8710e768303863f05c634101ecab0723c10e370c3b8c643ff30f96e"
+        "f159c1ac4f33a49c5c9833e06a2606e2fc7ede812e119909eb45f95dc2aa01fc"
 
     /**
      * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -139,16 +139,16 @@ public data class InlineMultipleWithDeferAndInclude(
      *     ... on Droid @skip(if: $cond2) {
      *       id
      *     }
-     *     ... on Droid @experimental_defer(label: "e_first") {
+     *     ... on Droid @defer(label: "e_first") {
      *       name
      *     }
-     *     ... on Droid @experimental_defer(label: "e_second") {
+     *     ... on Droid @defer(label: "e_second") {
      *       id
      *     }
-     *     ... on Droid @experimental_defer(label: "e_third") @include(if: $cond3) {
+     *     ... on Droid @defer(label: "e_third") @include(if: $cond3) {
      *       name
      *     }
-     *     ... on Droid @experimental_defer(label: "e_fourth") @skip(if: $cond4) {
+     *     ... on Droid @defer(label: "e_fourth") @skip(if: $cond4) {
      *       id
      *     }
      *   }
@@ -156,7 +156,7 @@ public data class InlineMultipleWithDeferAndInclude(
      */
     public val OPERATION_DOCUMENT: String
       get() =
-          "query InlineMultipleWithDeferAndInclude(${'$'}cond1: Boolean!, ${'$'}cond2: Boolean!, ${'$'}cond3: Boolean!, ${'$'}cond4: Boolean!) { hero { __typename ... on Droid @include(if: ${'$'}cond1) { name } ... on Droid @skip(if: ${'$'}cond2) { id } ... on Droid @experimental_defer(label: \"e_first\") { name } ... on Droid @experimental_defer(label: \"e_second\") { id } ... on Droid @experimental_defer(label: \"e_third\") @include(if: ${'$'}cond3) { name } ... on Droid @experimental_defer(label: \"e_fourth\") @skip(if: ${'$'}cond4) { id } } }"
+          "query InlineMultipleWithDeferAndInclude(${'$'}cond1: Boolean!, ${'$'}cond2: Boolean!, ${'$'}cond3: Boolean!, ${'$'}cond4: Boolean!) { hero { __typename ... on Droid @include(if: ${'$'}cond1) { name } ... on Droid @skip(if: ${'$'}cond2) { id } ... on Droid @defer(label: \"e_first\") { name } ... on Droid @defer(label: \"e_second\") { id } ... on Droid @defer(label: \"e_third\") @include(if: ${'$'}cond3) { name } ... on Droid @defer(label: \"e_fourth\") @skip(if: ${'$'}cond4) { id } } }"
 
     public const val OPERATION_NAME: String = "InlineMultipleWithDeferAndInclude"
   }
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDeferWithIf.kt.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDeferWithIf.kt.expected
index 1d6012561a..9acd69c4a7 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDeferWithIf.kt.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/InlineMultipleWithDeferWithIf.kt.expected
@@ -87,7 +87,7 @@ public data class InlineMultipleWithDeferWithIf(
 
   public companion object {
     public const val OPERATION_ID: String =
-        "3e777ab8534ca2847df43e92a828787127e9536d7c1ab8136985620443c2b991"
+        "9dc79c78df7375c3d860d16df3ea839ea45c7f8420976f63414610d338f0caf1"
 
     /**
      * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -96,13 +96,13 @@ public data class InlineMultipleWithDeferWithIf(
      * query InlineMultipleWithDeferWithIf($cond: Boolean!) {
      *   hero {
      *     __typename
-     *     ... on Character @experimental_defer(label: "f_first", if: false) {
+     *     ... on Character @defer(label: "f_first", if: false) {
      *       id
      *     }
-     *     ... on Character @experimental_defer(label: "f_second", if: true) {
+     *     ... on Character @defer(label: "f_second", if: true) {
      *       name
      *     }
-     *     ... on Character @experimental_defer(label: "f_third", if: $cond) {
+     *     ... on Character @defer(label: "f_third", if: $cond) {
      *       name
      *     }
      *   }
@@ -110,7 +110,7 @@ public data class InlineMultipleWithDeferWithIf(
      */
     public val OPERATION_DOCUMENT: String
       get() =
-          "query InlineMultipleWithDeferWithIf(${'$'}cond: Boolean!) { hero { __typename ... on Character @experimental_defer(label: \"f_first\", if: false) { id } ... on Character @experimental_defer(label: \"f_second\", if: true) { name } ... on Character @experimental_defer(label: \"f_third\", if: ${'$'}cond) { name } } }"
+          "query InlineMultipleWithDeferWithIf(${'$'}cond: Boolean!) { hero { __typename ... on Character @defer(label: \"f_first\", if: false) { id } ... on Character @defer(label: \"f_second\", if: true) { name } ... on Character @defer(label: \"f_third\", if: ${'$'}cond) { name } } }"
 
     public const val OPERATION_NAME: String = "InlineMultipleWithDeferWithIf"
   }
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/InlineSingleWithDefer.kt.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/InlineSingleWithDefer.kt.expected
index ee0e6530cf..e47c40d32f 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/InlineSingleWithDefer.kt.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/InlineSingleWithDefer.kt.expected
@@ -67,7 +67,7 @@ public class InlineSingleWithDefer() : Query<InlineSingleWithDefer.Data> {
 
   public companion object {
     public const val OPERATION_ID: String =
-        "da1ea6b7b54f4bc6e0754b9bbe45a002500fef00de7f3605a4efd02133c20def"
+        "70222802c9192f810f19a54d31e039ce0fc3b8baac231841f0a0e8b4200c312b"
 
     /**
      * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -76,15 +76,14 @@ public class InlineSingleWithDefer() : Query<InlineSingleWithDefer.Data> {
      * query InlineSingleWithDefer {
      *   hero {
      *     __typename
-     *     ... on Droid @experimental_defer {
+     *     ... on Droid @defer {
      *       name
      *     }
      *   }
      * }
      */
     public val OPERATION_DOCUMENT: String
-      get() =
-          "query InlineSingleWithDefer { hero { __typename ... on Droid @experimental_defer { name } } }"
+      get() = "query InlineSingleWithDefer { hero { __typename ... on Droid @defer { name } } }"
 
     public const val OPERATION_NAME: String = "InlineSingleWithDefer"
   }
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDefer.kt.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDefer.kt.expected
index cb61f5460e..80ba3e6853 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDefer.kt.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDefer.kt.expected
@@ -61,7 +61,7 @@ public class SpreadMultipleWithDefer() : Query<SpreadMultipleWithDefer.Data> {
 
   public companion object {
     public const val OPERATION_ID: String =
-        "e52d7b5c6a99f54c7d8e7f1d6c4f6e604586b4c83b212436661068e98f4f7ad1"
+        "7cd4d1b6a14f96cff4bbbdbd3b3ab1aa365ffc65e1515ff6e22935010543ad71"
 
     /**
      * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -70,8 +70,8 @@ public class SpreadMultipleWithDefer() : Query<SpreadMultipleWithDefer.Data> {
      * query SpreadMultipleWithDefer {
      *   hero {
      *     __typename
-     *     ...DroidDetails @experimental_defer(label: "a_first")
-     *     ...DroidDetails @experimental_defer(label: "a_second")
+     *     ...DroidDetails @defer(label: "a_first")
+     *     ...DroidDetails @defer(label: "a_second")
      *   }
      * }
      *
@@ -81,7 +81,7 @@ public class SpreadMultipleWithDefer() : Query<SpreadMultipleWithDefer.Data> {
      */
     public val OPERATION_DOCUMENT: String
       get() =
-          "query SpreadMultipleWithDefer { hero { __typename ...DroidDetails @experimental_defer(label: \"a_first\") ...DroidDetails @experimental_defer(label: \"a_second\") } }  fragment DroidDetails on Droid { name }"
+          "query SpreadMultipleWithDefer { hero { __typename ...DroidDetails @defer(label: \"a_first\") ...DroidDetails @defer(label: \"a_second\") } }  fragment DroidDetails on Droid { name }"
 
     public const val OPERATION_NAME: String = "SpreadMultipleWithDefer"
   }
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferAndInclude.kt.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferAndInclude.kt.expected
index 7e23cf33c9..a83d2a9dac 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferAndInclude.kt.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferAndInclude.kt.expected
@@ -62,7 +62,7 @@ public data class SpreadMultipleWithDeferAndInclude(
 
   public companion object {
     public const val OPERATION_ID: String =
-        "de31db86e7b05f57e3d3d700a4811e4aba6c56860dc18f94879165a5e04fcfe8"
+        "1c9d61b4af65df2bfed92353244efbb66d20a35daf747507e66ed9d20dcd1517"
 
     /**
      * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -74,10 +74,10 @@ public data class SpreadMultipleWithDeferAndInclude(
      *     __typename
      *     ...DroidDetails @include(if: $cond1)
      *     ...DroidDetails @skip(if: $cond2)
-     *     ...DroidDetails @experimental_defer(label: "b_first")
-     *     ...DroidDetails @experimental_defer(label: "b_second")
-     *     ...DroidDetails @experimental_defer(label: "b_third") @include(if: $cond3)
-     *     ...DroidDetails @experimental_defer(label: "b_fourth") @skip(if: $cond4)
+     *     ...DroidDetails @defer(label: "b_first")
+     *     ...DroidDetails @defer(label: "b_second")
+     *     ...DroidDetails @defer(label: "b_third") @include(if: $cond3)
+     *     ...DroidDetails @defer(label: "b_fourth") @skip(if: $cond4)
      *   }
      * }
      *
@@ -87,7 +87,7 @@ public data class SpreadMultipleWithDeferAndInclude(
      */
     public val OPERATION_DOCUMENT: String
       get() =
-          "query SpreadMultipleWithDeferAndInclude(${'$'}cond1: Boolean!, ${'$'}cond2: Boolean!, ${'$'}cond3: Boolean!, ${'$'}cond4: Boolean!) { hero { __typename ...DroidDetails @include(if: ${'$'}cond1) ...DroidDetails @skip(if: ${'$'}cond2) ...DroidDetails @experimental_defer(label: \"b_first\") ...DroidDetails @experimental_defer(label: \"b_second\") ...DroidDetails @experimental_defer(label: \"b_third\") @include(if: ${'$'}cond3) ...DroidDetails @experimental_defer(label: \"b_fourth\") @skip(if: ${'$'}cond4) } }  fragment DroidDetails on Droid { name }"
+          "query SpreadMultipleWithDeferAndInclude(${'$'}cond1: Boolean!, ${'$'}cond2: Boolean!, ${'$'}cond3: Boolean!, ${'$'}cond4: Boolean!) { hero { __typename ...DroidDetails @include(if: ${'$'}cond1) ...DroidDetails @skip(if: ${'$'}cond2) ...DroidDetails @defer(label: \"b_first\") ...DroidDetails @defer(label: \"b_second\") ...DroidDetails @defer(label: \"b_third\") @include(if: ${'$'}cond3) ...DroidDetails @defer(label: \"b_fourth\") @skip(if: ${'$'}cond4) } }  fragment DroidDetails on Droid { name }"
 
     public const val OPERATION_NAME: String = "SpreadMultipleWithDeferAndInclude"
   }
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferWithIf.kt.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferWithIf.kt.expected
index 36c8a748eb..96791e8f4f 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferWithIf.kt.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/SpreadMultipleWithDeferWithIf.kt.expected
@@ -69,7 +69,7 @@ public data class SpreadMultipleWithDeferWithIf(
 
   public companion object {
     public const val OPERATION_ID: String =
-        "f98db9747dd000723759253dacdd7a4e45eaa6003e459c001799901f96abbd8f"
+        "ecdac17ca37d6cbf3a6e5059904fabfd96193cbe0720eb27880026b518feec9f"
 
     /**
      * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -78,9 +78,9 @@ public data class SpreadMultipleWithDeferWithIf(
      * query SpreadMultipleWithDeferWithIf($cond: Boolean!) {
      *   hero {
      *     __typename
-     *     ...CharacterDetails @experimental_defer(label: "c_first", if: false)
-     *     ...CharacterDetails2 @experimental_defer(label: "c_second", if: true)
-     *     ...CharacterDetails3 @experimental_defer(label: "c_third", if: $cond)
+     *     ...CharacterDetails @defer(label: "c_first", if: false)
+     *     ...CharacterDetails2 @defer(label: "c_second", if: true)
+     *     ...CharacterDetails3 @defer(label: "c_third", if: $cond)
      *   }
      * }
      *
@@ -98,7 +98,7 @@ public data class SpreadMultipleWithDeferWithIf(
      */
     public val OPERATION_DOCUMENT: String
       get() =
-          "query SpreadMultipleWithDeferWithIf(${'$'}cond: Boolean!) { hero { __typename ...CharacterDetails @experimental_defer(label: \"c_first\", if: false) ...CharacterDetails2 @experimental_defer(label: \"c_second\", if: true) ...CharacterDetails3 @experimental_defer(label: \"c_third\", if: ${'$'}cond) } }  fragment CharacterDetails on Droid { name }  fragment CharacterDetails2 on Droid { id }  fragment CharacterDetails3 on Droid { birthDate }"
+          "query SpreadMultipleWithDeferWithIf(${'$'}cond: Boolean!) { hero { __typename ...CharacterDetails @defer(label: \"c_first\", if: false) ...CharacterDetails2 @defer(label: \"c_second\", if: true) ...CharacterDetails3 @defer(label: \"c_third\", if: ${'$'}cond) } }  fragment CharacterDetails on Droid { name }  fragment CharacterDetails2 on Droid { id }  fragment CharacterDetails3 on Droid { birthDate }"
 
     public const val OPERATION_NAME: String = "SpreadMultipleWithDeferWithIf"
   }
diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/SpreadSingleWithDefer.kt.expected b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/SpreadSingleWithDefer.kt.expected
index 7599625ee0..8e18f4b914 100644
--- a/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/SpreadSingleWithDefer.kt.expected
+++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_defer_and_include_directives/kotlin/operationBased/fragments_with_defer_and_include_directives/SpreadSingleWithDefer.kt.expected
@@ -61,7 +61,7 @@ public class SpreadSingleWithDefer() : Query<SpreadSingleWithDefer.Data> {
 
   public companion object {
     public const val OPERATION_ID: String =
-        "d7b739ea7224dce16c1992e5df95207a06f4f92b8231db42098755afac7a6439"
+        "fad1c3622a6f35d5fa4ad7b5e4a8d50525c350d4688837022f0f7a6f0aed2ba0"
 
     /**
      * The minimized GraphQL document being sent to the server to save a few bytes.
@@ -70,7 +70,7 @@ public class SpreadSingleWithDefer() : Query<SpreadSingleWithDefer.Data> {
      * query SpreadSingleWithDefer {
      *   hero {
      *     __typename
-     *     ...DroidDetails @experimental_defer
+     *     ...DroidDetails @defer
      *   }
      * }
      *
@@ -80,7 +80,7 @@ public class SpreadSingleWithDefer() : Query<SpreadSingleWithDefer.Data> {
      */
     public val OPERATION_DOCUMENT: String
       get() =
-          "query SpreadSingleWithDefer { hero { __typename ...DroidDetails @experimental_defer } }  fragment DroidDetails on Droid { name }"
+          "query SpreadSingleWithDefer { hero { __typename ...DroidDetails @defer } }  fragment DroidDetails on Droid { name }"
 
     public const val OPERATION_NAME: String = "SpreadSingleWithDefer"
   }
diff --git a/apollo-compiler/src/test/graphql/com/example/measurements b/apollo-compiler/src/test/graphql/com/example/measurements
index 712cba10c3..86f64abfee 100644
--- a/apollo-compiler/src/test/graphql/com/example/measurements
+++ b/apollo-compiler/src/test/graphql/com/example/measurements
@@ -2,11 +2,11 @@
 // If you updated the codegen and test fixtures, you should commit this file too.
 
 Test:                                                                                      Total LOC:
-aggregate-all                                                                                  194981
-aggregate-kotlin-responseBased                                                                  50287
+aggregate-all                                                                                  195000
+aggregate-kotlin-responseBased                                                                  50302
 aggregate-kotlin-operationBased                                                                 35446
 aggregate-kotlin-compat                                                                         36831
-aggregate-java-operationBased                                                                   72417
+aggregate-java-operationBased                                                                   72421
 
 java-operationBased-fragments_with_defer_and_include_directives                                  5392
 kotlin-compat-fragments_with_defer_and_include_directives                                        3613
@@ -22,7 +22,7 @@ java-operationBased-root_query_fragment_with_nested_fragments
 kotlin-compat-fragment_with_inline_fragment                                                      1486
 java-operationBased-mutation_create_review                                                       1447
 java-operationBased-named_fragment_delegate                                                      1445
-java-operationBased-__schema                                                                     1378
+java-operationBased-__schema                                                                     1382
 kotlin-responseBased-named_fragment_delegate                                                     1339
 java-operationBased-named_fragment_with_variables                                                1325
 kotlin-responseBased-unique_type_name                                                            1323
@@ -46,8 +46,8 @@ kotlin-compat-unique_type_name
 kotlin-operationBased-inline_fragment_intersection                                               1111
 kotlin-operationBased-union_inline_fragments                                                     1111
 kotlin-responseBased-fragment_used_twice                                                         1108
+kotlin-responseBased-__schema                                                                    1107
 kotlin-responseBased-union_inline_fragments                                                      1100
-kotlin-responseBased-__schema                                                                    1092
 java-operationBased-target_name                                                                  1086
 kotlin-operationBased-unique_type_name                                                           1080
 java-operationBased-root_query_inline_fragment                                                   1079
diff --git a/apollo-compiler/src/test/kotlin/com/apollographql/apollo3/compiler/FieldsOnDisjointTypesMustMergeTest.kt b/apollo-compiler/src/test/kotlin/com/apollographql/apollo3/compiler/FieldsOnDisjointTypesMustMergeTest.kt
new file mode 100644
index 0000000000..cd903582f0
--- /dev/null
+++ b/apollo-compiler/src/test/kotlin/com/apollographql/apollo3/compiler/FieldsOnDisjointTypesMustMergeTest.kt
@@ -0,0 +1,33 @@
+package com.apollographql.apollo3.compiler
+
+import com.apollographql.apollo3.ast.parseAsGQLDocument
+import com.apollographql.apollo3.ast.validateAsExecutable
+import com.apollographql.apollo3.compiler.TestUtils.checkExpected
+import com.apollographql.apollo3.compiler.TestUtils.findSchema
+import com.apollographql.apollo3.compiler.TestUtils.serialize
+import okio.buffer
+import okio.source
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import java.io.File
+
+class FieldsOnDisjointTypesMustMergeTest {
+  private val graphQLFile = File("src/test/validation/operation/fields_on_disjoint_types_must_merge/different_shapes.graphql")
+
+  @Test
+  fun testEnableFieldsOnDisjointTypesMustMerge() = checkExpected(graphQLFile) { schema ->
+    val parseResult = graphQLFile.source().buffer().parseAsGQLDocument(filePath = graphQLFile.name)
+    val issues = parseResult.valueAssertNoErrors().validateAsExecutable(schema = schema!!, fieldsOnDisjointTypesMustMerge = true).issues
+    issues.serialize()
+  }
+
+  @Test
+  fun testDisableFieldsOnDisjointTypesMustMerge() {
+    val schema = findSchema(graphQLFile.parentFile)!!
+    val parseResult = graphQLFile.source().buffer().parseAsGQLDocument(filePath = graphQLFile.name)
+
+    val issues = parseResult.valueAssertNoErrors().validateAsExecutable(schema = schema, fieldsOnDisjointTypesMustMerge = false).issues
+
+    assertTrue(issues.isEmpty())
+  }
+}
diff --git a/apollo-compiler/src/test/kotlin/com/apollographql/apollo3/compiler/TestUtils.kt b/apollo-compiler/src/test/kotlin/com/apollographql/apollo3/compiler/TestUtils.kt
index 4f6fe91138..923526ea97 100644
--- a/apollo-compiler/src/test/kotlin/com/apollographql/apollo3/compiler/TestUtils.kt
+++ b/apollo-compiler/src/test/kotlin/com/apollographql/apollo3/compiler/TestUtils.kt
@@ -1,15 +1,16 @@
 package com.apollographql.apollo3.compiler
 
+import com.apollographql.apollo3.ast.Issue
 import com.apollographql.apollo3.ast.Schema
-import com.apollographql.apollo3.ast.introspection.toSchema
 import com.apollographql.apollo3.ast.introspection.toSchemaGQLDocument
-import com.apollographql.apollo3.ast.validateAsSchema
 import com.apollographql.apollo3.ast.validateAsSchemaAndAddApolloDefinition
 import com.google.common.truth.Truth.assertThat
 import okio.Buffer
 import java.io.File
 
 internal object TestUtils {
+  private const val separator = "\n------------\n"
+
   internal fun shouldUpdateTestFixtures(): Boolean {
     if (System.getenv("updateTestFixtures") != null) {
       return true
@@ -67,7 +68,7 @@ internal object TestUtils {
     return File(parentFile, "$nameWithoutExtension.$newExtension")
   }
 
-  private fun findSchema(dir: File): Schema? {
+  fun findSchema(dir: File): Schema? {
     return listOf("graphqls", "sdl", "json").map { File(dir, "schema.$it") }
         .firstOrNull { it.exists() }
         ?.let {
@@ -114,6 +115,10 @@ internal object TestUtils {
       assertThat(actual).isEqualTo(expected)
     }
   }
+
+  fun List<Issue>.serialize() = joinToString(separator) {
+    "${it.severity}: ${it.javaClass.simpleName} (${it.sourceLocation.line}:${it.sourceLocation.position})\n${it.message}"
+  }
 }
 
 internal fun String.buffer() = Buffer().writeUtf8(this)
diff --git a/apollo-compiler/src/test/kotlin/com/apollographql/apollo3/compiler/ValidationTest.kt b/apollo-compiler/src/test/kotlin/com/apollographql/apollo3/compiler/ValidationTest.kt
index 167b147ff5..228b3dc357 100644
--- a/apollo-compiler/src/test/kotlin/com/apollographql/apollo3/compiler/ValidationTest.kt
+++ b/apollo-compiler/src/test/kotlin/com/apollographql/apollo3/compiler/ValidationTest.kt
@@ -1,10 +1,10 @@
 package com.apollographql.apollo3.compiler
 
-import com.apollographql.apollo3.ast.Issue
 import com.apollographql.apollo3.ast.parseAsGQLDocument
 import com.apollographql.apollo3.ast.validateAsExecutable
 import com.apollographql.apollo3.ast.validateAsSchemaAndAddApolloDefinition
 import com.apollographql.apollo3.compiler.TestUtils.checkExpected
+import com.apollographql.apollo3.compiler.TestUtils.serialize
 import com.apollographql.apollo3.compiler.TestUtils.testParametersForGraphQLFilesIn
 import okio.buffer
 import okio.source
@@ -16,11 +16,6 @@ import java.io.File
 @Suppress("UNUSED_PARAMETER")
 @RunWith(Parameterized::class)
 class ValidationTest(name: String, private val graphQLFile: File) {
-  private val separator = "\n------------\n"
-
-  private fun List<Issue>.serialize() = joinToString(separator) {
-    "${it.severity}: ${it.javaClass.simpleName} (${it.sourceLocation.line}:${it.sourceLocation.position})\n${it.message}"
-  }
 
   @Test
   fun testValidation() = checkExpected(graphQLFile) { schema ->
@@ -31,7 +26,8 @@ class ValidationTest(name: String, private val graphQLFile: File) {
       if (parseResult.issues.isNotEmpty()) {
         parseResult.issues
       } else {
-        parseResult.valueAssertNoErrors().validateAsExecutable(schema = schema!!).issues + if (graphQLFile.name == "capitalized_fields_disallowed.graphql") {
+        val mustMerge = graphQLFile.name != "merging_allowed.graphql"
+        parseResult.valueAssertNoErrors().validateAsExecutable(schema = schema!!, fieldsOnDisjointTypesMustMerge = mustMerge).issues + if (graphQLFile.name == "capitalized_fields_disallowed.graphql") {
           checkCapitalizedFields(parseResult.value!!.definitions)
         } else {
           emptyList()
diff --git a/apollo-compiler/src/test/kotlin/com/apollographql/apollo3/compiler/conditionalFragments/defer.graphql b/apollo-compiler/src/test/kotlin/com/apollographql/apollo3/compiler/conditionalFragments/defer.graphql
index f4bf4129a3..5422c866c0 100644
--- a/apollo-compiler/src/test/kotlin/com/apollographql/apollo3/compiler/conditionalFragments/defer.graphql
+++ b/apollo-compiler/src/test/kotlin/com/apollographql/apollo3/compiler/conditionalFragments/defer.graphql
@@ -1,6 +1,6 @@
 query QueryWithDefer($withDetails: Boolean!) {
   field1
-  ... on Query @experimental_defer(if: $withDetails) {
+  ... on Query @defer(if: $withDetails) {
     field2
   }
 }
diff --git a/apollo-compiler/src/test/validation/operation/defer/duplicate_labels.graphql b/apollo-compiler/src/test/validation/operation/defer/duplicate_labels.graphql
index 407e056bdd..e610fbb55d 100644
--- a/apollo-compiler/src/test/validation/operation/defer/duplicate_labels.graphql
+++ b/apollo-compiler/src/test/validation/operation/defer/duplicate_labels.graphql
@@ -1,12 +1,12 @@
 query WithFragmentSpreads {
   computers {
-    ...ComputerFields @experimental_defer(label: "foobar")
+    ...ComputerFields @defer(label: "foobar")
   }
 }
 
 fragment ComputerFields on Computer {
   screen {
-    ...ScreenFields @experimental_defer(label: "foobar")
+    ...ScreenFields @defer(label: "foobar")
   }
 }
 
diff --git a/apollo-compiler/src/test/validation/operation/defer/duplicate_path_and_labels_inline.graphql b/apollo-compiler/src/test/validation/operation/defer/duplicate_path_and_labels_inline.graphql
index e92bbe2dc5..f0e9b6e4c6 100644
--- a/apollo-compiler/src/test/validation/operation/defer/duplicate_path_and_labels_inline.graphql
+++ b/apollo-compiler/src/test/validation/operation/defer/duplicate_path_and_labels_inline.graphql
@@ -6,10 +6,10 @@ query WithInlineFragments {
       year
       screen {
         resolution
-        ... on Screen @experimental_defer {
+        ... on Screen @defer {
           isColor
         }
-        ... on HasId @experimental_defer {
+        ... on HasId @defer {
           id
         }
       }
diff --git a/apollo-compiler/src/test/validation/operation/defer/duplicate_path_and_labels_spread.graphql b/apollo-compiler/src/test/validation/operation/defer/duplicate_path_and_labels_spread.graphql
index e632ecca53..35c439e7c0 100644
--- a/apollo-compiler/src/test/validation/operation/defer/duplicate_path_and_labels_spread.graphql
+++ b/apollo-compiler/src/test/validation/operation/defer/duplicate_path_and_labels_spread.graphql
@@ -7,13 +7,13 @@ query WithFragmentSpreads {
 
 fragment ComputerFields on Computer {
   screen {
-    ...ScreenFields @experimental_defer
+    ...ScreenFields @defer
   }
 }
 
 fragment ComputerFields2 on Computer {
   screen {
-    ...ScreenFields @experimental_defer
+    ...ScreenFields @defer
   }
 }
 
diff --git a/apollo-compiler/src/test/validation/operation/defer/fragment_used_twice_not_duplicate_labels.graphql b/apollo-compiler/src/test/validation/operation/defer/fragment_used_twice_not_duplicate_labels.graphql
index 13d8f5607d..37e766f87b 100644
--- a/apollo-compiler/src/test/validation/operation/defer/fragment_used_twice_not_duplicate_labels.graphql
+++ b/apollo-compiler/src/test/validation/operation/defer/fragment_used_twice_not_duplicate_labels.graphql
@@ -1,14 +1,14 @@
 query Query1 {
   computers {
     id
-    ...ComputerFields @experimental_defer
+    ...ComputerFields @defer
   }
 }
 
 query Query2 {
   computers {
     id
-    ...ComputerFields @experimental_defer
+    ...ComputerFields @defer
   }
 }
 
@@ -18,7 +18,7 @@ fragment ComputerFields on Computer {
   screen {
     resolution
     # This is traversed twice, but it's not a duplicate label
-    ...ScreenFields @experimental_defer(label: "a")
+    ...ScreenFields @defer(label: "a")
   }
 }
 
diff --git a/apollo-compiler/src/test/validation/operation/defer/illegal_label.graphql b/apollo-compiler/src/test/validation/operation/defer/illegal_label.graphql
index 9a17a78441..3c865e04a4 100644
--- a/apollo-compiler/src/test/validation/operation/defer/illegal_label.graphql
+++ b/apollo-compiler/src/test/validation/operation/defer/illegal_label.graphql
@@ -1,6 +1,6 @@
 query GetAnimal {
   animal {
-    ... on Cat @experimental_defer(label: "a/b") {
+    ... on Cat @defer(label: "a/b") {
       meow
     }
   }
diff --git a/apollo-compiler/src/test/validation/operation/defer/label_is_a_variable.graphql b/apollo-compiler/src/test/validation/operation/defer/label_is_a_variable.graphql
index 5b73ff9759..e334978700 100644
--- a/apollo-compiler/src/test/validation/operation/defer/label_is_a_variable.graphql
+++ b/apollo-compiler/src/test/validation/operation/defer/label_is_a_variable.graphql
@@ -1,6 +1,6 @@
 query GetAnimal($label: String) {
   animal {
-    ... on Cat @experimental_defer(label: $label) {
+    ... on Cat @defer(label: $label) {
       meow
     }
   }
diff --git a/apollo-compiler/src/test/validation/operation/defer/root_field_mutation_or_subscription.graphql b/apollo-compiler/src/test/validation/operation/defer/root_field_mutation_or_subscription.graphql
index 5dbac92583..c55d0590cc 100644
--- a/apollo-compiler/src/test/validation/operation/defer/root_field_mutation_or_subscription.graphql
+++ b/apollo-compiler/src/test/validation/operation/defer/root_field_mutation_or_subscription.graphql
@@ -1,5 +1,5 @@
 query GetAnimalQuery {
-  ... on Query @experimental_defer {
+  ... on Query @defer {
     animal {
       id
     }
@@ -7,7 +7,7 @@ query GetAnimalQuery {
 }
 
 subscription GetAnimalSubscription {
-  ... on Subscription @experimental_defer {
+  ... on Subscription @defer {
     animal {
       id
     }
@@ -15,7 +15,7 @@ subscription GetAnimalSubscription {
 }
 
 mutation CreateAnimalMutation {
-  ... on Mutation @experimental_defer {
+  ... on Mutation @defer {
     createSession
   }
 }
diff --git a/apollo-compiler/src/test/validation/operation/fields_in_set_can_merge/merging_allowed.expected b/apollo-compiler/src/test/validation/operation/fields_in_set_can_merge/merging_allowed.expected
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/apollo-compiler/src/test/validation/operation/fields_in_set_can_merge/merging_allowed.graphql b/apollo-compiler/src/test/validation/operation/fields_in_set_can_merge/merging_allowed.graphql
new file mode 100644
index 0000000000..8ada2689f9
--- /dev/null
+++ b/apollo-compiler/src/test/validation/operation/fields_in_set_can_merge/merging_allowed.graphql
@@ -0,0 +1,10 @@
+query GetAnimal {
+    animal {
+        ... on Cat {
+            meow
+        }
+        ... on Dog {
+            meow: id
+        }
+    }
+}
\ No newline at end of file
diff --git a/apollo-compiler/src/test/validation/operation/fields_on_disjoint_types_must_merge/different_shapes.expected b/apollo-compiler/src/test/validation/operation/fields_on_disjoint_types_must_merge/different_shapes.expected
new file mode 100644
index 0000000000..056e136ebf
--- /dev/null
+++ b/apollo-compiler/src/test/validation/operation/fields_on_disjoint_types_must_merge/different_shapes.expected
@@ -0,0 +1,11 @@
+ERROR: ValidationError (9:6)
+`value` cannot be merged with `value` (at different_shapes.graphql: (12, 7)): they have different shapes. Use different aliases on the fields to fetch both if this was intentional.
+------------
+ERROR: ValidationError (12:6)
+`value` cannot be merged with `value` (at different_shapes.graphql: (9, 7)): they have different shapes. Use different aliases on the fields to fetch both if this was intentional.
+------------
+ERROR: ValidationError (9:6)
+`value` cannot be merged with `value` (at different_shapes.graphql: (12, 7)): they have different shapes. Use different aliases on the fields to fetch both if this was intentional.
+------------
+ERROR: ValidationError (12:6)
+`value` cannot be merged with `value` (at different_shapes.graphql: (9, 7)): they have different shapes. Use different aliases on the fields to fetch both if this was intentional.
\ No newline at end of file
diff --git a/apollo-compiler/src/test/validation/operation/fields_on_disjoint_types_must_merge/different_shapes.graphql b/apollo-compiler/src/test/validation/operation/fields_on_disjoint_types_must_merge/different_shapes.graphql
new file mode 100644
index 0000000000..92f3c2b2ed
--- /dev/null
+++ b/apollo-compiler/src/test/validation/operation/fields_on_disjoint_types_must_merge/different_shapes.graphql
@@ -0,0 +1,14 @@
+query GetFoo {
+  foo {
+    ...PropertyValue
+  }
+}
+fragment PropertyValue on PropertyValue {
+  __typename
+  ... on PropertyValueBoolean {
+      value
+  }
+  ... on PropertyValueInt {
+      value
+  }
+}
\ No newline at end of file
diff --git a/apollo-compiler/src/test/validation/operation/fields_on_disjoint_types_must_merge/schema.graphqls b/apollo-compiler/src/test/validation/operation/fields_on_disjoint_types_must_merge/schema.graphqls
new file mode 100644
index 0000000000..98daea9cd1
--- /dev/null
+++ b/apollo-compiler/src/test/validation/operation/fields_on_disjoint_types_must_merge/schema.graphqls
@@ -0,0 +1,13 @@
+type Query {
+  foo: PropertyValue
+}
+union PropertyValue = PropertyValueBoolean | PropertyValueInt
+
+type PropertyValueBoolean {
+  name: String!
+  value: Boolean
+}
+type PropertyValueInt {
+  name: String!
+  value: Int
+}
\ No newline at end of file
diff --git a/apollo-runtime/src/commonTest/kotlin/com/apollographql/apollo3/internal/DeferJsonMergerTest.kt b/apollo-runtime/src/commonTest/kotlin/com/apollographql/apollo3/internal/DeferJsonMergerTest.kt
index 4264f9eb75..5b06a81f20 100644
--- a/apollo-runtime/src/commonTest/kotlin/com/apollographql/apollo3/internal/DeferJsonMergerTest.kt
+++ b/apollo-runtime/src/commonTest/kotlin/com/apollographql/apollo3/internal/DeferJsonMergerTest.kt
@@ -12,11 +12,10 @@ import kotlin.test.assertEquals
 
 class DeferJsonMergerTest {
   @Test
-  @Suppress("UNCHECKED_CAST")
-  fun mergeJson() {
-    val deferredJsonMerger = DeferredJsonMerger()
+  fun mergeJsonSingleIncrementalItem() {
+      val deferredJsonMerger = DeferredJsonMerger()
 
-    val payload1 = """
+      val payload1 = """
       {
         "data": {
           "computers": [
@@ -44,25 +43,29 @@ class DeferJsonMergerTest {
 
     val payload2 = """
       {
-        "data": {
-          "cpu": "386",
-          "year": 1993,
-          "screen": {
-            "resolution": "640x480"
+        "incremental": [
+          {
+            "data": {
+              "cpu": "386",
+              "year": 1993,
+              "screen": {
+                "resolution": "640x480"
+              }
+            },
+            "path": [
+              "computers",
+              0
+            ],
+            "label": "query:Query1:0",
+            "extensions": {
+              "duration": {
+                "amount": 100,
+                "unit": "ms"
+              }
+            }
           }
-        },
-        "path": [
-          "computers",
-          0
         ],
-        "label": "query:Query1:0",
-        "hasNext": true,
-        "extensions": {
-          "duration": {
-            "amount": 100,
-            "unit": "ms"
-          }
-        }
+        "hasNext": true
       }
     """
     val mergedPayloads_1_2 = """
@@ -88,10 +91,14 @@ class DeferJsonMergerTest {
         },
         "hasNext": true,
         "extensions": {
-          "duration": {
-            "amount": 100,
-            "unit": "ms"
-          }
+          "incremental": [
+            {
+              "duration": {
+                "amount": 100,
+                "unit": "ms"
+              }
+            }
+          ]
         }
       }      
     """
@@ -103,27 +110,31 @@ class DeferJsonMergerTest {
 
 
     val payload3 = """
-      {
-        "data": {
-          "cpu": "486",
-          "year": 1996,
-          "screen": {
-            "resolution": "640x480"
-          }
-        },
-        "path": [
-          "computers",
-          1
-        ],
-        "label": "query:Query1:0",
-        "hasNext": true,
-        "extensions": {
-          "duration": {
-            "amount": 25,
-            "unit": "ms"
-          }
+        {
+          "incremental": [
+            {
+              "data": {
+                "cpu": "486",
+                "year": 1996,
+                "screen": {
+                  "resolution": "640x480"
+                }
+              },
+              "path": [
+                "computers",
+                1
+              ],
+              "label": "query:Query1:0",
+              "extensions": {
+                "duration": {
+                  "amount": 25,
+                  "unit": "ms"
+                }
+              }
+            }
+          ],
+          "hasNext": true
         }
-      }
     """
     val mergedPayloads_1_2_3 = """
       {
@@ -151,10 +162,14 @@ class DeferJsonMergerTest {
         },
         "hasNext": true,
         "extensions": {
-          "duration": {
-            "amount": 25,
-            "unit": "ms"
-          }
+          "incremental": [
+            {
+              "duration": {
+                "amount": 25,
+                "unit": "ms"
+              }
+            }
+          ]
         }
       }
     """
@@ -167,33 +182,37 @@ class DeferJsonMergerTest {
 
 
     val payload4 = """
-      {
-        "data": null,
-        "path": [
-          "computers",
-          0,
-          "screen"
-        ],
-        "errors": [
-          {
-            "message": "Cannot resolve isColor",
-            "locations": [
-              {
-                "line": 12,
-                "column": 11
-              }
-            ],
-            "path": [
-              "computers",
-              0,
-              "screen",
-              "isColor"
-            ]
-          }
-        ],
-        "label": "fragment:ComputerFields:0",
-        "hasNext": true
-      }
+        {
+          "incremental": [
+            {
+              "data": null,
+              "path": [
+                "computers",
+                0,
+                "screen"
+              ],
+              "errors": [
+                {
+                  "message": "Cannot resolve isColor",
+                  "locations": [
+                    {
+                      "line": 12,
+                      "column": 11
+                    }
+                  ],
+                  "path": [
+                    "computers",
+                    0,
+                    "screen",
+                    "isColor"
+                  ]
+                }
+              ],
+              "label": "fragment:ComputerFields:0"
+            }
+          ],
+          "hasNext": true
+        }
     """
     val mergedPayloads_1_2_3_4 = """
       {
@@ -246,16 +265,80 @@ class DeferJsonMergerTest {
         DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"),
     ), deferredJsonMerger.mergedFragmentIds)
 
+
     val payload5 = """
+        {
+          "incremental": [
+            {
+              "data": {
+                "isColor": false
+              },
+              "path": [
+                "computers",
+                1,
+                "screen"
+              ],
+              "errors": [
+                {
+                  "message": "Another error",
+                  "locations": [
+                    {
+                      "line": 1,
+                      "column": 1
+                    }
+                  ]
+                }
+              ],
+              "label": "fragment:ComputerFields:0",
+              "extensions": {
+                "value": 42,
+                "duration": {
+                  "amount": 130,
+                  "unit": "ms"
+                }
+              }
+            }
+          ],
+          "hasNext": false
+        }
+    """
+    val mergedPayloads_1_2_3_4_5 = """
       {
         "data": {
-          "isColor": false
+          "computers": [
+            {
+              "id": "Computer1",
+              "cpu": "386",
+              "year": 1993,
+              "screen": {
+                "isTouch": true,
+                "resolution": "640x480"
+              }
+            },
+            {
+              "id": "Computer2",
+              "cpu": "486",
+              "year": 1996,
+              "screen": {
+                "isTouch": false,
+                "resolution": "640x480",
+                "isColor": false
+              }
+            }
+          ]
+        },
+        "hasNext": true,
+        "extensions": {
+          "incremental": [
+            {
+              "value": 42,
+              "duration": {
+                "amount": 130,
+                "unit": "ms"
+              }
+            }
+          ]
         },
-        "path": [
-          "computers",
-          1,
-          "screen"
-        ],
         "errors": [
           {
             "message": "Another error",
@@ -266,19 +349,209 @@ class DeferJsonMergerTest {
               }
             ]
           }
-        ],
-        "label": "fragment:ComputerFields:0",
-        "hasNext": false,
-        "extensions": {
-          "value": 42,
-          "duration": {
-            "amount": 130,
-            "unit": "ms"
+        ]
+      }
+    """
+      deferredJsonMerger.merge(payload5.buffer())
+      assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), deferredJsonMerger.merged)
+      assertEquals(setOf(
+          DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"),
+          DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"),
+          DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"),
+      ), deferredJsonMerger.mergedFragmentIds)
+  }
+
+    @Test
+    fun mergeJsonMultipleIncrementalItems() {
+        val deferredJsonMerger = DeferredJsonMerger()
+
+        val payload1 = """
+      {
+        "data": {
+          "computers": [
+            {
+              "id": "Computer1",
+              "screen": {
+                "isTouch": true
+              }
+            },
+            {
+              "id": "Computer2",
+              "screen": {
+                "isTouch": false
+              }
+            }
+          ]
+        },
+        "hasNext": true
+      }
+    """
+        deferredJsonMerger.merge(payload1.buffer())
+        assertEquals(jsonToMap(payload1), deferredJsonMerger.merged)
+        assertEquals(setOf(), deferredJsonMerger.mergedFragmentIds)
+
+
+        val payload2_3 = """
+    {
+      "incremental": [
+        {
+          "data": {
+            "cpu": "386",
+            "year": 1993,
+            "screen": {
+              "resolution": "640x480"
+            }
+          },
+          "path": [
+            "computers",
+            0
+          ],
+          "label": "query:Query1:0",
+          "extensions": {
+            "duration": {
+              "amount": 100,
+              "unit": "ms"
+            }
+          }
+        },
+        {
+          "data": {
+            "cpu": "486",
+            "year": 1996,
+            "screen": {
+              "resolution": "640x480"
+            }
+          },
+          "path": [
+            "computers",
+            1
+          ],
+          "label": "query:Query1:0",
+          "extensions": {
+            "duration": {
+              "amount": 25,
+              "unit": "ms"
+            }
           }
         }
+      ],
+      "hasNext": true
+    }
+    """
+        val mergedPayloads_1_2_3 = """
+      {
+        "data": {
+          "computers": [
+            {
+              "id": "Computer1",
+              "cpu": "386",
+              "year": 1993,
+              "screen": {
+                "isTouch": true,
+                "resolution": "640x480"
+              }
+            },
+            {
+              "id": "Computer2",
+              "cpu": "486",
+              "year": 1996,
+              "screen": {
+                "isTouch": false,
+                "resolution": "640x480"
+              }
+            }
+          ]
+        },
+        "hasNext": true,
+        "extensions": {
+          "incremental": [
+            {
+              "duration": {
+                "amount": 100,
+                "unit": "ms"
+              }
+            },
+            {
+              "duration": {
+                "amount": 25,
+                "unit": "ms"
+              }
+            }
+          ]
+        }
       }
     """
-    val mergedPayloads_1_2_3_4_5 = """
+        deferredJsonMerger.merge(payload2_3.buffer())
+        assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged)
+        assertEquals(setOf(
+            DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"),
+            DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"),
+        ), deferredJsonMerger.mergedFragmentIds)
+
+
+        val payload4_5 = """
+      {
+        "incremental": [
+          {
+            "data": null,
+            "path": [
+              "computers",
+              0,
+              "screen"
+            ],
+            "errors": [
+              {
+                "message": "Cannot resolve isColor",
+                "locations": [
+                  {
+                    "line": 12,
+                    "column": 11
+                  }
+                ],
+                "path": [
+                  "computers",
+                  0,
+                  "screen",
+                  "isColor"
+                ]
+              }
+            ],
+            "label": "fragment:ComputerFields:0"
+          },
+          {
+            "data": {
+              "isColor": false
+            },
+            "path": [
+              "computers",
+              1,
+              "screen"
+            ],
+            "errors": [
+              {
+                "message": "Another error",
+                "locations": [
+                  {
+                    "line": 1,
+                    "column": 1
+                  }
+                ]
+              }
+            ],
+            "label": "fragment:ComputerFields:0",
+            "extensions": {
+              "value": 42,
+              "duration": {
+                "amount": 130,
+                "unit": "ms"
+              }
+            }
+          }
+        ],
+        "hasNext": true
+      }
+    """
+        val mergedPayloads_1_2_3_4_5 = """
       {
         "data": {
           "computers": [
@@ -305,13 +578,32 @@ class DeferJsonMergerTest {
         },
         "hasNext": true,
         "extensions": {
-          "value": 42,
-          "duration": {
-            "amount": 130,
-            "unit": "ms"
-          }
+          "incremental": [
+            {
+              "value": 42,
+              "duration": {
+                "amount": 130,
+                "unit": "ms"
+              }
+            }
+          ]
         },
         "errors": [
+          {
+            "message": "Cannot resolve isColor",
+            "locations": [
+              {
+                "line": 12,
+                "column": 11
+              }
+            ],
+            "path": [
+              "computers",
+              0,
+              "screen",
+              "isColor"
+            ]
+          },
           {
             "message": "Another error",
             "locations": [
@@ -324,14 +616,14 @@ class DeferJsonMergerTest {
         ]
       }
     """
-    deferredJsonMerger.merge(payload5.buffer())
-    assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), deferredJsonMerger.merged)
-    assertEquals(setOf(
-        DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"),
-        DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"),
-        DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"),
-    ), deferredJsonMerger.mergedFragmentIds)
-  }
+        deferredJsonMerger.merge(payload4_5.buffer())
+        assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), deferredJsonMerger.merged)
+        assertEquals(setOf(
+            DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"),
+            DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"),
+            DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"),
+        ), deferredJsonMerger.mergedFragmentIds)
+    }
 }
 
 private fun String.buffer() = Buffer().writeUtf8(this)
diff --git a/tests/data-builders-java/src/test/java/test/DataBuilderTest.java b/tests/data-builders-java/src/test/java/test/DataBuilderTest.java
index 63fdf34641..bcc04606f6 100644
--- a/tests/data-builders-java/src/test/java/test/DataBuilderTest.java
+++ b/tests/data-builders-java/src/test/java/test/DataBuilderTest.java
@@ -202,7 +202,7 @@ public class DataBuilderTest {
       Object ret = null;
       switch (name) {
         case "Long1": {
-          ret = "45";
+          ret = new MyLong(45L);
           break;
         }
         case "Long2": {
diff --git a/tests/data-builders-kotlin/build.gradle.kts b/tests/data-builders-kotlin/build.gradle.kts
index 705e38bc71..d94ce148f9 100644
--- a/tests/data-builders-kotlin/build.gradle.kts
+++ b/tests/data-builders-kotlin/build.gradle.kts
@@ -14,6 +14,6 @@ apollo {
   packageName.set("data.builders")
   generateDataBuilders.set(true)
   addTypename.set("always")
-  mapScalar("Long1", "MyLong", "MyLongAdapter")
-  mapScalar("Long2", "MyLong")
+  mapScalar("Long1", "com.example.MyLong", "com.example.MyLongAdapter")
+  mapScalar("Long2", "com.example.MyLong")
 }
diff --git a/tests/data-builders-kotlin/src/main/kotlin/MyLong.kt b/tests/data-builders-kotlin/src/main/kotlin/com/example/MyLong.kt
similarity index 96%
rename from tests/data-builders-kotlin/src/main/kotlin/MyLong.kt
rename to tests/data-builders-kotlin/src/main/kotlin/com/example/MyLong.kt
index 2a1f875855..b8f83419d0 100644
--- a/tests/data-builders-kotlin/src/main/kotlin/MyLong.kt
+++ b/tests/data-builders-kotlin/src/main/kotlin/com/example/MyLong.kt
@@ -1,3 +1,5 @@
+package com.example
+
 import com.apollographql.apollo3.api.Adapter
 import com.apollographql.apollo3.api.CustomScalarAdapters
 import com.apollographql.apollo3.api.json.JsonReader
diff --git a/tests/data-builders-kotlin/src/test/kotlin/test/DataBuilderTest.kt b/tests/data-builders-kotlin/src/test/kotlin/test/DataBuilderTest.kt
index dbcf989500..b6ac4cd451 100644
--- a/tests/data-builders-kotlin/src/test/kotlin/test/DataBuilderTest.kt
+++ b/tests/data-builders-kotlin/src/test/kotlin/test/DataBuilderTest.kt
@@ -1,8 +1,8 @@
 package test
 
-import MyLong
 import com.apollographql.apollo3.api.FakeResolver
 import com.apollographql.apollo3.api.FakeResolverContext
+import com.example.MyLong
 import data.builders.GetAliasesQuery
 import data.builders.GetAnimalQuery
 import data.builders.GetCustomScalarQuery
@@ -13,7 +13,6 @@ import data.builders.GetIntQuery
 import data.builders.GetPartialQuery
 import data.builders.PutIntMutation
 import data.builders.type.Direction
-import data.builders.type.Lion
 import data.builders.type.buildCat
 import data.builders.type.buildLion
 import kotlin.test.Test
@@ -170,7 +169,7 @@ class DataBuilderTest {
   class MyFakeResolver : FakeResolver {
     override fun resolveLeaf(context: FakeResolverContext): Any {
       return when (context.mergedField.type.leafType().name) {
-        "Long1" -> "45" // build-time => this needs to be resolved to Json
+        "Long1" -> MyLong(45) // build-time
         "Long2" -> MyLong(46) // run-time
         "Long3" -> 47L // mapped to Any
         else -> error("")
diff --git a/tests/defer/build.gradle.kts b/tests/defer/build.gradle.kts
index 3877bb7aac..7ae10eb41e 100644
--- a/tests/defer/build.gradle.kts
+++ b/tests/defer/build.gradle.kts
@@ -33,7 +33,7 @@ kotlin {
 
     val jsTest by getting {
       dependencies {
-        implementation(npm("graphql-helix", "1.12.0"))
+        implementation(npm("graphql-helix", "1.13.0"))
         implementation(npm("express", "4.17.3"))
         implementation(npm("ws", "8.2.2"))
         implementation(npm("graphql-ws", "5.5.0"))
diff --git a/tests/defer/src/commonMain/graphql/operation.graphql b/tests/defer/src/commonMain/graphql/operation.graphql
index 1e5a09cf5e..a8f3dfb722 100644
--- a/tests/defer/src/commonMain/graphql/operation.graphql
+++ b/tests/defer/src/commonMain/graphql/operation.graphql
@@ -1,7 +1,7 @@
 query WithFragmentSpreadsQuery {
   computers {
     id
-    ...ComputerFields @experimental_defer
+    ...ComputerFields @defer
   }
 }
 
@@ -10,7 +10,7 @@ fragment ComputerFields on Computer {
   year
   screen {
     resolution
-    ...ScreenFields @experimental_defer(label: "a")
+    ...ScreenFields @defer(label: "a")
   }
 }
 
@@ -22,12 +22,12 @@ fragment ScreenFields on Screen {
 query WithInlineFragmentsQuery {
   computers {
     id
-    ... on Computer @experimental_defer {
+    ... on Computer @defer {
       cpu
       year
       screen {
         resolution
-        ... on Screen @experimental_defer(label: "b") {
+        ... on Screen @defer(label: "b") {
           isColor
         }
       }
@@ -39,14 +39,14 @@ query WithInlineFragmentsQuery {
 mutation WithFragmentSpreadsMutation {
   computers {
     id
-    ...ComputerFields @experimental_defer(label: "c")
+    ...ComputerFields @defer(label: "c")
   }
 }
 
 subscription WithInlineFragmentsSubscription {
   count(to: 3) {
     value
-    ... on Counter @experimental_defer {
+    ... on Counter @defer {
       valueTimesTwo
     }
   }
@@ -55,7 +55,7 @@ subscription WithInlineFragmentsSubscription {
 subscription WithFragmentSpreadsSubscription {
   count(to: 3) {
     value
-    ...CounterFields @experimental_defer(label: "d")
+    ...CounterFields @defer(label: "d")
   }
 }
 
diff --git a/tests/defer/src/commonTest/kotlin/test/DeferNormalizedCacheTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferNormalizedCacheTest.kt
index 419417e6a7..d07d464e13 100644
--- a/tests/defer/src/commonTest/kotlin/test/DeferNormalizedCacheTest.kt
+++ b/tests/defer/src/commonTest/kotlin/test/DeferNormalizedCacheTest.kt
@@ -66,8 +66,8 @@ class DeferNormalizedCacheTest {
     // Fill the cache by doing a network only request
     val jsonList = listOf(
         """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""",
-        """{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0],"hasNext":true}""",
-        """{"data":{"isColor":false},"path":["computers",0,"screen"],"hasNext":false,"label":"a"}""",
+        """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""",
+        """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""",
     )
     mockServer.enqueueMultipart(jsonList)
     apolloClient.query(WithFragmentSpreadsQuery()).fetchPolicy(FetchPolicy.NetworkOnly).toFlow().collect()
@@ -93,8 +93,8 @@ class DeferNormalizedCacheTest {
     // Fill the cache by doing a first request
     val jsonList = listOf(
         """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""",
-        """{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0],"hasNext":true}""",
-        """{"data":{"isColor":false},"path":["computers",0,"screen"],"hasNext":false,"label":"a"}""",
+        """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""",
+        """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""",
     )
     mockServer.enqueueMultipart(jsonList)
     apolloClient.query(WithFragmentSpreadsQuery()).fetchPolicy(FetchPolicy.NetworkOnly).toFlow().collect()
@@ -128,8 +128,8 @@ class DeferNormalizedCacheTest {
 
     val jsonList = listOf(
         """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""",
-        """{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0],"hasNext":true}""",
-        """{"data":{"isColor":false},"path":["computers",0,"screen"],"hasNext":false,"label":"a"}""",
+        """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""",
+        """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""",
     )
     mockServer.enqueueMultipart(jsonList)
 
@@ -168,8 +168,8 @@ class DeferNormalizedCacheTest {
 
     val jsonList = listOf(
         """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""",
-        """{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0],"hasNext":true}""",
-        """{"data":{"isColor":false},"path":["computers",0,"screen"],"hasNext":false,"label":"a"}""",
+        """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""",
+        """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""",
     )
     mockServer.enqueueMultipart(jsonList)
 
@@ -208,8 +208,8 @@ class DeferNormalizedCacheTest {
 
     val jsonList1 = listOf(
         """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""",
-        """{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0],"hasNext":true}""",
-        """{"data":{"isColor":false},"path":["computers",0,"screen"],"hasNext":false,"label":"a"}""",
+        """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""",
+        """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""",
     )
     mockServer.enqueueMultipart(jsonList1)
 
@@ -235,8 +235,8 @@ class DeferNormalizedCacheTest {
 
     val jsonList2 = listOf(
         """{"data":{"computers":[{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""",
-        """{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",0],"hasNext":true}""",
-        """{"data":{"isColor":true},"path":["computers",0,"screen"],"hasNext":false,"label":"a"}""",
+        """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",0]}],"hasNext":true}""",
+        """{"incremental": [{"data":{"isColor":true},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""",
     )
     mockServer.enqueueMultipart(jsonList2)
 
@@ -271,8 +271,8 @@ class DeferNormalizedCacheTest {
 
     val jsonList = listOf(
         """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""",
-        """{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0],"hasNext":true}""",
-        """{"data":null,"path":["computers",0,"screen"],"label":"b","errors":[{"message":"Cannot resolve isColor","locations":[{"line":1,"column":119}],"path":["computers",0,"screen","isColor"]}],"hasNext":false}""",
+        """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""",
+        """{"incremental": [{"data":null,"path":["computers",0,"screen"],"label":"b","errors":[{"message":"Cannot resolve isColor","locations":[{"line":1,"column":119}],"path":["computers",0,"screen","isColor"]}]}],"hasNext":false}""",
     )
     mockServer.enqueueMultipart(jsonList)
 
@@ -402,8 +402,8 @@ class DeferNormalizedCacheTest {
   fun mutation() = runTest(before = { setUp() }, after = { tearDown() }) {
     val jsonList = listOf(
         """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""",
-        """{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0],"hasNext":true,"label":"c"}""",
-        """{"data":{"isColor":false},"path":["computers",0,"screen"],"hasNext":false,"label":"a"}""",
+        """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0],"label":"c"}],"hasNext":true}""",
+        """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""",
     )
     mockServer.enqueueMultipart(jsonList)
     val networkActual = apolloClient.mutation(WithFragmentSpreadsMutation()).toFlow().toList().map { it.dataAssertNoErrors }
@@ -441,8 +441,8 @@ class DeferNormalizedCacheTest {
   fun mutationWithOptimisticDataFails() = runTest(before = { setUp() }, after = { tearDown() }) {
     val jsonList = listOf(
         """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""",
-        """{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0],"hasNext":true,"label":"c"}""",
-        """{"data":{"isColor":false},"path":["computers",0,"screen"],"hasNext":false,"label":"a"}""",
+        """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0],"label":"c"}],"hasNext":true}""",
+        """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""",
     )
     mockServer.enqueueMultipart(jsonList)
     val responses = apolloClient.mutation(WithFragmentSpreadsMutation()).optimisticUpdates(
diff --git a/tests/defer/src/commonTest/kotlin/test/DeferTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferTest.kt
index 047568d029..069e01ff62 100644
--- a/tests/defer/src/commonTest/kotlin/test/DeferTest.kt
+++ b/tests/defer/src/commonTest/kotlin/test/DeferTest.kt
@@ -42,10 +42,10 @@ class DeferTest {
   fun deferWithFragmentSpreads() = runTest(before = { setUp() }, after = { tearDown() }) {
     val jsonList = listOf(
         """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""",
-        """{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0],"hasNext":true}""",
-        """{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1],"hasNext":true}""",
-        """{"data":{"isColor":false},"path":["computers",0,"screen"],"hasNext":true,"label":"a"}""",
-        """{"data":{"isColor":true},"path":["computers",1,"screen"],"hasNext":false,"label":"a"}""",
+        """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""",
+        """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""",
+        """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":true}""",
+        """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""",
     )
 
     val expectedDataList = listOf(
@@ -100,10 +100,10 @@ class DeferTest {
   fun deferWithInlineFragments() = runTest(before = { setUp() }, after = { tearDown() }) {
     val jsonList = listOf(
         """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""",
-        """{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0],"hasNext":true}""",
-        """{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1],"hasNext":true}""",
-        """{"data":{"isColor":false},"path":["computers",0,"screen"],"hasNext":true,"label":"b"}""",
-        """{"data":{"isColor":true},"path":["computers",1,"screen"],"hasNext":false,"label":"b"}""",
+        """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""",
+        """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""",
+        """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"b"}],"hasNext":true}""",
+        """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"b"}],"hasNext":false}""",
     )
 
     val expectedDataList = listOf(
@@ -158,10 +158,10 @@ class DeferTest {
   fun deferWithFragmentSpreadsAndError() = runTest(before = { setUp() }, after = { tearDown() }) {
     val jsonList = listOf(
         """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""",
-        """{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0],"hasNext":true}""",
-        """{"data":null,"path":["computers",0,"screen"],"label":"b","errors":[{"message":"Cannot resolve isColor","locations":[{"line":1,"column":119}],"path":["computers",0,"screen","isColor"]}],"hasNext":true}""",
-        """{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1],"hasNext":true}""",
-        """{"data":{"isColor":true},"path":["computers",1,"screen"],"hasNext":false,"label":"a"}""",
+        """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""",
+        """{"incremental": [{"data":null,"path":["computers",0,"screen"],"label":"b","errors":[{"message":"Cannot resolve isColor","locations":[{"line":1,"column":119}],"path":["computers",0,"screen","isColor"]}]}],"hasNext":true}""",
+        """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""",
+        """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""",
     )
 
     val query = WithFragmentSpreadsQuery()
@@ -266,10 +266,10 @@ class DeferTest {
 
     val jsonList = listOf(
         """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""",
-        """{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0],"hasNext":true}""",
-        """{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1],"hasNext":true}""",
-        """{"data":{"isColor":false},"path":["computers",0,"screen"],"hasNext":true,"label":"a"}""",
-        """{"data":{"isColor":true},"path":["computers",1,"screen"],"hasNext":false,"label":"a"}""",
+        """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""",
+        """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""",
+        """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":true}""",
+        """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""",
     )
 
     for ((index, json) in jsonList.withIndex()) {
diff --git a/tests/defer/src/jsTest/kotlin/helix/HelixServer.kt b/tests/defer/src/jsTest/kotlin/helix/HelixServer.kt
index fccb3c38e4..44663a08c3 100644
--- a/tests/defer/src/jsTest/kotlin/helix/HelixServer.kt
+++ b/tests/defer/src/jsTest/kotlin/helix/HelixServer.kt
@@ -35,7 +35,7 @@ class HelixServer(
       if (shouldRenderGraphiQL(request)) {
         res.send(
             renderGraphiQL(dynamicObject {
-              subscriptionsEndpoint = "ws://localhost:4000/graphql"
+              subscriptionsEndpoint = "ws://localhost:$port/graphql"
             })
         )
         return@use null
diff --git a/tests/defer/src/jsTest/kotlin/test/DeferWithHelixTest.kt b/tests/defer/src/jsTest/kotlin/test/DeferWithHelixTest.kt
index da5d17cfbd..9b8fe4c711 100644
--- a/tests/defer/src/jsTest/kotlin/test/DeferWithHelixTest.kt
+++ b/tests/defer/src/jsTest/kotlin/test/DeferWithHelixTest.kt
@@ -26,19 +26,21 @@ import helix.HelixServer
 import kotlinx.coroutines.flow.toList
 import util.dynamicObject
 import util.jsAsyncIterator
+import kotlin.random.Random
 import kotlin.test.Ignore
 import kotlin.test.Test
 import kotlin.test.assertEquals
 
 @Ignore
-// TODO These tests are temporarily ignored while @defer has been renamed to @experimental_defer
-// See https://github.com/apollographql/apollo-kotlin/issues/4071
+// TODO These tests are temporarily ignored as the latest version of graphql-js
+// uses a package format which is incompatible with Kotlin/JS.
+// See https://youtrack.jetbrains.com/issue/KT-12784
 class DeferWithHelixTest {
   private lateinit var helixServer: HelixServer
   private lateinit var apolloClient: ApolloClient
 
   private suspend fun setUp() {
-    helixServer = HelixServer(schema)
+    helixServer = HelixServer(schema, port = Random.nextInt(1024, 65535))
     apolloClient = ApolloClient.Builder()
         .serverUrl(helixServer.url())
         .webSocketServerUrl(helixServer.webSocketUrl())
diff --git a/tests/defer/src/jvmTest/kotlin/test/DeferJvmTest.kt b/tests/defer/src/jvmTest/kotlin/test/DeferJvmTest.kt
index 29e4781a11..6eba9627bf 100644
--- a/tests/defer/src/jvmTest/kotlin/test/DeferJvmTest.kt
+++ b/tests/defer/src/jvmTest/kotlin/test/DeferJvmTest.kt
@@ -52,10 +52,10 @@ class DeferJvmTest {
 
     val jsonList = listOf(
         """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""",
-        """{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0],"hasNext":true}""",
-        """{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1],"hasNext":true}""",
-        """{"data":{"isColor":false},"path":["computers",0,"screen"],"hasNext":true,"label":"a"}""",
-        """{"data":{"isColor":true},"path":["computers",1,"screen"],"hasNext":false,"label":"a"}""",
+        """{"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""",
+        """{"incremental":[{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""",
+        """{"incremental":[{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":true}""",
+        """{"incremental":[{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""",
     )
 
     for ((index, json) in jsonList.withIndex()) {
diff --git a/tests/integration-tests/src/commonTest/kotlin/test/LoggingInterceptorTest.kt b/tests/integration-tests/src/commonTest/kotlin/test/LoggingInterceptorTest.kt
index 9b4db6bcae..5ae24cad9b 100644
--- a/tests/integration-tests/src/commonTest/kotlin/test/LoggingInterceptorTest.kt
+++ b/tests/integration-tests/src/commonTest/kotlin/test/LoggingInterceptorTest.kt
@@ -83,6 +83,7 @@ class LoggingInterceptorTest {
       Post http://0.0.0.0/
       X-APOLLO-OPERATION-ID: 7e7c85cbf5ef3af5641552c55965608a4e5d7243f3116a486d21c3a958d34235
       X-APOLLO-OPERATION-NAME: HeroName
+      accept: multipart/mixed; deferspec=20220822, application/json
       [end of headers]
 
       HTTP: 200
@@ -103,6 +104,7 @@ class LoggingInterceptorTest {
       Post http://0.0.0.0/
       X-APOLLO-OPERATION-ID: 7e7c85cbf5ef3af5641552c55965608a4e5d7243f3116a486d21c3a958d34235
       X-APOLLO-OPERATION-NAME: HeroName
+      accept: multipart/mixed; deferspec=20220822, application/json
       [end of headers]
       {"operationName":"HeroName","variables":{},"query":"query HeroName { hero { name } }"}
 
diff --git a/tests/kotlin-js-store-1.7/yarn.lock b/tests/kotlin-js-store-1.7/yarn.lock
index 0385ad5239..dc980300d0 100644
--- a/tests/kotlin-js-store-1.7/yarn.lock
+++ b/tests/kotlin-js-store-1.7/yarn.lock
@@ -411,10 +411,10 @@ google-protobuf@3.12.2:
   resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.12.2.tgz#50ce9f9b6281235724eb243d6a83e969a2176e53"
   integrity sha512-4CZhpuRr1d6HjlyrxoXoocoGFnRYgKULgMtikMddA9ztRyYR59Aondv2FioyxWVamRo0rF2XpYawkTCBEQOSkA==
 
-graphql-helix@1.12.0:
-  version "1.12.0"
-  resolved "https://registry.yarnpkg.com/graphql-helix/-/graphql-helix-1.12.0.tgz#8a81e7c84489a0c6da968afdb54f9a979512baf9"
-  integrity sha512-wTu1m/ZFBCgzLPCg/dHO4cNy9JR5Sq/3RqDVblqrU3b/Yem7EXZtQMzG045tHqkCdbgcgQl+kIkXDur1yLfdtw==
+graphql-helix@1.13.0:
+  version "1.13.0"
+  resolved "https://registry.yarnpkg.com/graphql-helix/-/graphql-helix-1.13.0.tgz#e64dad5ef5f622ef38c97fa033f56f3d953c0104"
+  integrity sha512-cqDKMoRywKjnL0ZWCTB0GOiBgsH6d3nU4JGDF6RuzAyd35tmalzKpSxkx3NNp4H5RvnKWnrukWzR51wUq277ng==
 
 graphql-ws@5.5.0:
   version "5.5.0"