-
Notifications
You must be signed in to change notification settings - Fork 13
Codegen Migration
This guide covers migrating an existing retrofit-graphql project from the asset-based workflow (@GraphQuery + QueryContainerBuilder + .graphql files in assets/) to the build-time code generation workflow with the backend-neutral request/response contracts.
Note: The asset-based workflow remains supported through the deprecated
:compatmodule. Code generation is an optional enhancement, not a replacement. You can also use both approaches in the same project.
Asset-Based (legacy :compat) |
Codegen (backend-neutral) |
|---|---|
@GraphQuery annotation on Retrofit methods |
Generated operation constants and request helpers |
QueryContainerBuilder for manual variable construction |
Typed variable classes with named parameters |
Runtime file discovery from assets/
|
Build-time document registry — no runtime file I/O |
| String-based variable keys | Type-safe variable classes |
| No enum generation | GraphQL enums generated as Kotlin enum classes |
GraphContainer<T> / GraphError responses |
GraphQLResponse<T> / GraphQLData (neutral) |
GraphConverter + GraphQLJson (or Gson) |
GraphQLConverterFactory + explicit GraphQLTransportCodec
|
plugins {
id("co.anitrend.retrofit.graphql.codegen") version "{tag}"
}Move your .graphql files from assets/graphql/ to src/main/graphql/:
# Before
app/src/main/assets/graphql/queries/GetMarketPlaceApps.graphql
# After
app/src/main/graphql/queries/GetMarketPlaceApps.graphql
Add the schema file:
app/src/main/graphql/schema.graphql
retrofitGraphQL {
packageName.set("your.package.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 response model generation
// serializationBackend is strict and used as-is:
// NONE stays plain (no auto-selection), KOTLINX emits @Serializable/@SerialName,
// GSON emits @SerializedName. Default is NONE.
}
scalars {
// Map any custom scalars in your schema
map("DateTime", "kotlin.String")
map("GitObjectID", "kotlin.String")
}
}Remove direct :android-assets and :annotations dependencies — they are no longer needed for codegen-only projects. Add an explicit runtime codec:
// Before
dependencies {
implementation(project(":runtime"))
implementation(project(":api"))
implementation(project(":android-assets"))
implementation(project(":annotations"))
}
// After
dependencies {
implementation(project(":runtime"))
implementation(project(":api"))
implementation(project(":serialization-kotlinx")) // or :serialization-gson
}
:runtimestill pulls:android-assetsand:annotationstransitively, so legacy asset-based fallback remains available if you keep:compaton the classpath.
Replace the legacy GraphConverter construction with the backend-neutral factory plus an explicit codec:
// Legacy (deprecated, :compat) -- only if you keep the GraphConverter/GraphContainer path
val legacyFactory = GraphConverter.create(
context = context,
registry = GeneratedGraphQLRegistry,
)
// Backend-neutral (new) -- explicit codec required; no default backend
val factory = GraphQLConverterFactory.create(
codec = KotlinxGraphQLTransportCodec(), // or GsonGraphQLTransportCodec()
registry = GeneratedGraphQLRegistry,
)Replace @GraphQuery + QueryContainerBuilder + GraphContainer with the neutral request/response contracts:
// Before (legacy, :compat)
@POST("/graphql")
@GraphQuery("GetMarketPlaceApps")
suspend fun getMarketPlaceApps(
@Body builder: QueryContainerBuilder,
): Response<GraphContainer<MarketPlaceListings>>
// After (backend-neutral)
@POST("/graphql")
suspend fun getMarketPlaceApps(
@Body request: GraphQLOperationRequest<GetMarketPlaceAppsVariables>,
): Response<GraphQLResponse<MarketPlaceListings>>If you enable generateResponses, you can also replace hand-written payload DTOs with generated operation-scoped response models:
suspend fun getMarketPlaceApps(
@Body request: GraphQLOperationRequest<GetMarketPlaceAppsVariables>,
): Response<GraphQLResponse<GetMarketPlaceAppsData>>Replace QueryContainerBuilder with generated request helpers, which return the neutral GraphQLOperationRequest:
// Before (legacy, :compat)
val builder = QueryContainerBuilder()
.putVariable("first", 15)
.putVariable("after", cursor)
service.getMarketPlaceApps(builder)
// After (backend-neutral)
val request = GetMarketPlaceApps.request(
first = 15,
after = cursor,
)
service.getMarketPlaceApps(request)For multipart uploads, add a RequestBodyPassThroughConverterFactory before the converter. The sample app keeps the bucket upload on the legacy GraphConverter (:compat) because the upload mutation uses the separate bucket schema:
// Legacy (deprecated, :compat) bucket endpoint in the sample app
Retrofit.Builder()
.addConverterFactory(RequestBodyPassThroughConverterFactory())
.addConverterFactory(GraphConverter.create(context, registry = GeneratedGraphQLRegistry))
.baseUrl(baseUrl)
.build()Use the generated operation metadata:
val request = UploadToStorageBucket.request(upload = filePath)
// Build MultipartBody from the generated request fieldsSee GraphQL File Uploads for details.
You don't have to migrate everything at once. Both workflows can coexist:
- Apply the codegen plugin alongside existing
@GraphQueryusage. - Migrate one operation at a time — remove
@GraphQueryfrom a method and switch toGraphQLOperationRequest+GraphQLResponse. - Enable
generateResponsesonly for operations where you want generated payload models. - Once all operations are migrated, remove the legacy
:compatdependency,:annotations, and:android-assetsdirect dependencies.
- Code Generation — Full codegen reference
- Getting Started — Quick start guide
-
Migration Guide — Migrating from
:libraryto modular dependencies - Serialization Backends — codegen annotation setting and runtime codecs