You're viewing documentation for a version of this software that is in development. Switch to the latest stable version
Launch Apollo Studio

Multi Modules


For multi-modules projects, Apollo Android allows to define queries in a feature module and reuse fragments and types from another parent module. This helps with better separation of concerns and build times.

Setup

Configure your parent module to generate Apollo metadata:

// parent/build.gradle.kts
apollo {
    generateApolloMetadata.set(true)
}

And declare your parent module as a dependency of your feature module:

// feature/build.gradle.kts
dependencies {
    implementation("com.apollographql.apollo3:apollo-runtime:xyz")
    // more regular dependencies
    
    // Apollo dependencies
    apolloMetadata(project(":parent"))
    // You still need to declare the parent module as a regular dependency
    implementation(project(":parent"))
}

Resolving Apollo dependencies

A feature module can have any number of parent modules. Parent modules themselves can also have parents, creating a graph of dependencies.

Transitive Apollo dependencies will always expose their fragments and types to the modules downstream. In other words, there is no implementation vs api concept like there is for regular dependencies. Apollo dependencies will always expose everything downstream (i.e are treated as api).

Another important thing to note is that all modules must share the same schema. Place schema.[json | graphqls] in the root module, the module that is the higher up in the dependencies graph:

// root/build.gradle[.kts]
// This module is the root module
// Place the schema in this module
dependencies {
    generateApolloMetadata.set(true)
}

// common/build.gradle[.kts]
// This module is an intermediate module
// It must not have a schema
// It can use fragments and types from 'root'
dependencies {
    apolloMetadata(project(":root"))
    generateApolloMetadata.set(true)
}

// feature/build.gradle[.kts]
// This module is a leaf module
// It must not have a schema
// It can use fragments and types from 'shared' and 'root'
dependencies {
    apolloMetadata(project(":shared"))
}

Multiplatform

For multiplatform projects, put apolloMetadata in the top level dependencies {} block:

// feature/build.gradle.kts
// This module must not have a schema
// This module can use fragments and types from 'shared' and 'root'
dependencies {
    apolloMetadata(project(":shared"))
}

kotlin {
    jvm()

    sourceSets {
        val commonMain by getting {
            dependencies {
                implementation("com.apollographql.apollo:apollo-api:x.y.z")
                api("com.apollographql.apollo:apollo-runtime-kotlin:x.y.z")
            }
        }
    }
}

Summary of different constraints:

  • All modules must apply the same version of the Apollo Gradle Plugin
  • All modules using the same schemas must use the same service name
  • The root module and only the root module must define schema.[json | graphqls]
  • The root module and only the root module must define customScalarsMapping
  • The root module and only the root module must define generateKotlinModels

Type clashes

When using multiple modules, Apollo Android will generate models for every operation and fragment defined in this module. In addition, it's going to generate classes for schema types used by these operation. For an example, for Input Objects, Enums, Custom Scalars, etc...

If two sibling modules use the same schema type and this schema type wasn't generated upstream, each module will generate its own version of the schema type which could clash. To prevent this, Apollo Android register a global "check${service}ApolloDuplicates" task that will fail if there are duplicates.

If that happens, you will need to resolve the type clash manually by forcing generation of the conflicting type in an upstream module. This is done using the alwaysGenerateTypesMatching Gradle option:

// parent/build.gradle.kts
apollo {
    generateApolloMetadata.set(true)
    // For an example if ReviewInput clashes
    alwaysGenerateTypesMatching.set(listOf("ReviewInput"))
    // You can also pass Regex patterns
    alwaysGenerateTypesMatching.set(listOf(".*Input"))
}
Edit on GitHub