Skip to content

Codegen

Maxwell edited this page Jun 23, 2026 · 3 revisions

Code Generation

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, and typed request helpers.

This is the recommended approach for new projects. It combines the flexibility of hand-written .graphql files with type-safe request construction, without generating full response models (unlike Apollo).

Quick Setup

1. Apply the plugin

plugins {
    id("co.anitrend.retrofit.graphql.codegen") version "{tag}"
}

2. Configure the DSL

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)
    }
    scalars {
        map("DateTime", "kotlin.String")
        map("GitObjectID", "kotlin.String")
        map("URI", "kotlin.String")
    }
}

3. Add runtime dependencies

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)
}

4. Wire the converter

val factory = GraphConverter.create(
    context = context,
    registry = GeneratedGraphQLRegistry,
)

DSL Options

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.generateHashes true Generate SHA-256 APQ hashes for operations
scalars { map(...) } (none) Map custom GraphQL scalars to Kotlin types

Generated Output

All output goes to build/generated/source/graphql/.

Always generated

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

Generated when generateVariables = true

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>

Using Generated Types

Queries without variables

// Generated: GetCurrentUser object with .document and .name properties
val request = GraphQLRequest(
    query = GetCurrentUser.document,
    operationName = GetCurrentUser.name,
)

Queries with variables

// Generated: GetMarketPlaceApps.request(first = 15, after = null)
val request = GetMarketPlaceApps.request(
    first = 15,
    after = "some_cursor",
)
// Returns GraphQLRequest<GetMarketPlaceAppsVariables>

Mutations with variables

// Generated: UploadToStorageBucket.request(upload = "/path/to/file")
val request = UploadToStorageBucket.request(
    upload = filePath,
)
// Returns GraphQLRequest<UploadToStorageBucketVariables>

Retrofit interface

interface GitHubService {
    @POST("/graphql")
    suspend fun getMarketPlaceApps(
        @Body request: GraphQLRequest<GetMarketPlaceAppsVariables>,
    ): Response<GraphContainer<MarketPlaceListings>>
}

Scalar Mappings

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.

Enum Generation

GraphQL enum types are generated as Kotlin enum classes:

enum OrderDirection {
  ASC
  DESC
}

Generates:

enum class OrderDirection {
    ASC,
    DESC,
}

How It Works (Internals)

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.

File Organization

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

Further Reading

Clone this wiki locally