You are viewing documentation for a previous version of this software.

Switch to the latest stable version.

Multi Modules (experimental)


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

Support for multi-modules build is currently experimental and we'd love to have your feedback on it. Things that worked well, things that can be improved, etc.. You can reach out either through Github issues or the Kotlinlang slack channel. We're looking forward to hearing from you!

Setup

Multi-modules requires that one and only one module contains a schema. This is the schema that all other modules can reuse. In this document, we'll refer to this module as the "schema module".

Configure your schema module to generate Apollo metadata:

Kotlin
1// schema/build.gradle.kts
2apollo {
3    generateApolloMetadata.set(true)
4}

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

Kotlin
1// feature/build.gradle.kts
2dependencies {
3    implementation("com.apollographql.apollo:apollo-runtime:xyz")
4    // more regular dependencies
5    
6    // Apollo dependencies
7    apolloMetadata(project(":schema"))
8    // You still need to declare the schema module as a regular dependency
9    implementation(project(":schema"))
10}

Resolving Apollo dependencies

A feature module can have any number of apollo dependencies, each one contributing their types and fragments.

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.[graphqls | json] in the schema module, the module that is the higher up in the dependencies graph:

Kotlin
1// feature/build.gradle.kts
2// This module must not have a schema
3// This module can use fragments and types from 'shared' and 'schema'
4dependencies {
5    apolloMetadata(project(":shared"))
6}
7
8// shared/build.gradle.kts
9// This module can use fragments and types from 'schema'
10dependencies {
11    apolloMetadata(project(":schema"))
12    generateApolloMetadata.set(true)
13}
14
15// schema/build.gradle.kts
16// This module is the schema module
17// Place the schema in this module 
18dependencies {
19    generateApolloMetadata.set(true)
20}

Summary of different constraints:

  • The schema module and only the schema module must define schema.[graphqls | json]

  • The schema module and only the schema module must define customTypeMapping

  • The schema module and only the schema module must define generateKotlinModels

  • The schema module must have a .graphql file. This file can be empty

  • All modules must apply the same version of the Apollo Gradle Plugin

  • Either all modules must apply the Android plugin or no module should apply it

Big schemas

When using multiple modules, Apollo Android will generate all possible Input Types and Enums in the schema module, and not only the used ones. This is because there is no way to know from the dependencies what types are going to be used in feature modules. By default:

  • All input objects and enums are generated in the schema module.

  • Fragments, queries, mutations and subscriptions are generated in the module where they are defined.

If your schema contains a lot of input objects, it can generate a lot of source files and increase compilation time beyond what's acceptable. To mitigate this, you have the option to control what types are generated with the alwaysGenerateTypesMatching option:

Kotlin
1// schema/build.gradle.kts
2apollo {
3    generateApolloMetadata.set(true)
4    // For the schema module, this defaults to [".*"]
5    // To use the single-module behaviour of only generating types that are actually used, pass en empty list
6    alwaysGenerateTypesMatching.set(emptyList())
7}

If the same input object or enum is used in two sibling modules, the same type would end up being generated twice and an error will happen. You can fix this by instructing an upstream module to generate the common type:

Kotlin
1// shared/build.gradle.kts
2apollo {
3    // Generate common types here
4    // Here we specify the names of the types 
5    alwaysGenerateTypesMatching.set(listOf("CommonInputType", "CommonEnum"))
6    // It also works with regexes
7    alwaysGenerateTypesMatching.set(listOf(".*Input"))
8}

Multiplatform

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

Kotlin
1// feature/build.gradle.kts
2// This module must not have a schema
3// This module can use fragments and types from 'shared' and 'schema'
4dependencies {
5    apolloMetadata(project(":shared"))
6}
7
8kotlin {
9    jvm()
10    
11    sourceSets {
12        val commonMain by getting {
13            dependencies {
14                implementation(project(":shared"))
15                implementation("com.apollographql.apollo:apollo-api:2.5.8")
16                api("com.apollographql.apollo:apollo-runtime-kotlin:2.5.8")
17            }
18        }
19    }
20}
Feedback

Edit on GitHub

Forums