backingData Directive
The @backingData directive binds a field to a backing data class that performs transformation, shaping, or
preparation of data before it is exposed by GraphQL. It complements @resolver: the resolver wires the field into
execution, while the backing data class centralizes the mapping logic that would otherwise live inside the resolver.
Basic usage
Apply @backingData on a field (often together with @resolver) and point to a Kotlin class that implements the
backing logic.
allCharacters(limit: Int): [Character]
@resolver
@backingData(class: "com.example.starwars.modules.filmography.characters.queries.AllCharactersQueryResolver")
In this demo, the class
com.example.starwars.modules.filmography.characters.queries.AllCharactersQueryResolver
is responsible for shaping the list of Character items returned by allCharacters.
How it integrates at runtime
- Execution plan: Viaduct identifies that
allCharactersis a resolver-backed field with a backing data class. - Resolver step: the resolver coordinates arguments and orchestration (for example, reads the
limit). - Backing step: the backing class transforms raw domain models into GraphQL objects (builders), applying any mapping, filtering, or normalization rules required by the schema.
- Result: the field returns objects that already match the schema’s expectations (IDs, formatting, minimal fields).
With @backingData (mapping lives in a backing class)
@Resolver
class AllCharactersQueryResolver : QueryResolvers.AllCharacters() {
override suspend fun resolve(ctx: Context): List<Character?>? {
// Fetch characters with pagination
val limit = ctx.arguments.limit ?: DEFAULT_PAGE_SIZE
val characters = CharacterRepository.findAll().take(limit)
// Convert StarWarsData.Character objects to Character objects
return characters.map { CharacterBuilder(ctx).build(it) }
}
}
Pros: fewer moving parts. Cons: resolver grows, mapping is harder to share or test in isolation.
Design guidelines
- Keep resolvers thin; put mapping/formatting in the backing data class.
- Generate
idvalues withctx.globalIDFor(Type.Reflection, internalId). - Request only the minimal fields you need; defer relationships to field/batch resolvers.
- Prefer immutable outputs from the backing class (builders with final values).
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.