-
Notifications
You must be signed in to change notification settings - Fork 13
Codegen
The retrofit-graphql codegen Gradle plugin (co.anitrend.retrofit.graphql.codegen) parses .graphql operation files at build time and generates Kotlin source code: operation constants, a document registry, enum classes, variable classes, input object classes, typed request helpers, and optional response model data classes.
This is the recommended approach for new projects. It combines the flexibility of hand-written .graphql files with type-safe request construction. Response models are opt-in, operation-scoped, and intentionally narrower than schema-wide generators such as Apollo.
plugins {
id("co.anitrend.retrofit.graphql.codegen") version "{tag}"
}retrofitGraphQL {
packageName.set("co.anitrend.retrofit.graphql.generated")
schema.set(file("src/main/graphql/schema.graphql"))
operations.from(fileTree("src/main/graphql") {
include("**/*.graphql")
})
common {
generateVariables.set(true)
generateResponses.set(true) // optional, default false
}
scalars {
map("DateTime", "kotlin.String")
map("GitObjectID", "kotlin.String")
map("URI", "kotlin.String")
}
}dependencies {
implementation("com.github.AniTrend.retrofit-graphql:runtime:{tag}")
implementation("com.github.AniTrend.retrofit-graphql:api:{tag}")
// :annotations and :android-assets are NOT required for codegen-only consumers
// (they are pulled transitively via :runtime)
}val factory = GraphConverter.create(
context = context,
registry = GeneratedGraphQLRegistry,
)| Option | Default | Description |
|---|---|---|
packageName |
(required) | Package for generated classes |
schema |
(required) | Path to schema.graphql
|
operations.from |
(required) | File tree of .graphql operation files |
common.generateVariables |
false |
Generate variable classes, input objects, and request helpers |
common.generateResponses |
false |
Generate kotlinx-serializable {OperationName}Data response model classes |
common.generateHashes |
true |
Generate SHA-256 APQ hashes for operations |
scalars { map(...) } |
(none) | Map custom GraphQL scalars to Kotlin types |
All output goes to build/generated/source/graphql/.
| Output | Description |
|---|---|
GeneratedGraphQLRegistry |
Implements GraphQLDocumentRegistry — provides document text and APQ hashes by operation name |
GraphQLOperations |
Operation name constants |
GraphQLDocuments |
Operation document text constants |
GraphQLHashes |
SHA-256 hash constants (when generateHashes = true) |
| Enum classes | GraphQL enum types generated as Kotlin enum classes |
| Output | Description |
|---|---|
| Variable classes | Data classes implementing GraphQLVariables for each operation's variables |
| Input object classes | Data classes for GraphQL input types referenced by operations |
| Request helpers |
.request(...) factory methods on operation objects, returning GraphQLRequest<VariableType>
|
| Output | Description |
|---|---|
{OperationName}Data |
Root response model for the operation |
| Nested object classes | Kotlin data classes for selected object fields |
| Sealed interfaces for abstract types |
interface/union hierarchies modeled with @JsonClassDiscriminator("__typename")
|
When generateResponses is enabled, the plugin generates kotlinx-serializable models from the operation selection set rather than from the full schema.
Generated response models:
- Match the exact JSON response shape for the operation
- Preserve aliases as distinct Kotlin properties
- Preserve list nullability for both the list container and elements
- Convert conditional selections (
@include/@skip) into nullable Kotlin properties withnulldefaults when needed - Generate sealed interfaces for GraphQL interfaces and unions using
__typenamediscrimination, with no globalJsonconfiguration required
retrofitGraphQL {
common {
generateResponses.set(true)
}
target("anilist") {
schema.set(file("src/main/graphql/schema.graphql"))
operations.from(fileTree("src/main/graphql"))
}
}Example generated root type:
@Serializable
data class GetCurrentUserData(
@SerialName("viewer")
val viewer: Viewer?,
)You can use the generated model as the GraphContainer payload type in your Retrofit service:
interface GitHubService {
@POST("/graphql")
suspend fun getCurrentUser(
@Body request: GraphQLRequest<Unit>,
): Response<GraphContainer<GetCurrentUserData>>
}The response-model pipeline uses a normalized intermediate representation:
-
RuntimePath— tracks which concrete runtime types are active at each abstract response path -
ResponseFieldVariant— keeps field variants separate until projection, instead of collapsing mutually exclusive selections too early - Projection phase — selects the active field variant set for one runtime path before Kotlin generation
-
ResponseModelIdentity— exact key used for nested model collection, deduplication, and property type resolution
These internals matter when you rely on nested interfaces, unions, aliased fields, or named fragments. The generated Kotlin types are operation-scoped and branch-aware.
// Generated: GetCurrentUser object with .document and .name properties
val request = GraphQLRequest(
query = GetCurrentUser.document,
operationName = GetCurrentUser.name,
)// Generated: GetMarketPlaceApps.request(first = 15, after = null)
val request = GetMarketPlaceApps.request(
first = 15,
after = "some_cursor",
)
// Returns GraphQLRequest<GetMarketPlaceAppsVariables>// Generated: UploadToStorageBucket.request(upload = "/path/to/file")
val request = UploadToStorageBucket.request(
upload = filePath,
)
// Returns GraphQLRequest<UploadToStorageBucketVariables>interface GitHubService {
@POST("/graphql")
suspend fun getMarketPlaceApps(
@Body request: GraphQLRequest<GetMarketPlaceAppsVariables>,
): Response<GraphContainer<MarketPlaceListings>>
}Custom GraphQL scalar types used in generated code must be mapped to Kotlin types. Unmapped scalars cause a build error with a path-aware message indicating where the unmapped scalar was encountered.
scalars {
map("DateTime", "kotlin.String")
map("GitObjectID", "kotlin.String")
map("URI", "kotlin.String")
}Scalars that are mapped but absent from the schema (e.g. "Upload") are allowed — they generate no type but do not fail the build.
GraphQL enum types are generated as Kotlin enum classes:
enum OrderDirection {
ASC
DESC
}Generates:
enum class OrderDirection {
ASC,
DESC,
}The codegen pipeline uses these internal components (consumers do not interact with these directly):
-
SchemaIndex— Typed schema metadata index, replacing a flat name set. Enables kind-aware lookups (scalar, enum, input object, etc.). -
GraphQLTypeMapper— Maps GraphQL types to Kotlin types, branching by schema kind. -
GraphQLDefaultValueRenderer— Renders default value literals for generated code, replacing duplicated heuristic converters. -
GraphQLTypeUsageValidator— Recursively validates that all scalar types in generated code have mappings, producing path-aware error messages. -
ResponseSelectionParser— Normalizes response selections, fragment scopes, aliases, and conditional fields before generation. -
ResponseModelGenerator— Projects normalized selections into concrete runtime branches and generates{OperationName}Datamodels.
Place .graphql files under src/main/graphql/:
src/main/graphql/
├── schema.graphql
├── queries/
│ ├── GetMarketPlaceApps.graphql
│ ├── GetCurrentUser.graphql
│ └── StorageBucketFiles.graphql
├── mutations/
│ └── UploadToStorageBucket.graphql
└── fragments/
└── MarketPlaceListingCore.graphql
-
Migration Guide — Migrating from
:libraryto modular dependencies - Switching to Codegen — Moving from asset-based to codegen workflow
- Getting Started — Quick start with codegen
- GraphQL File Uploads — Multipart upload with generated types