OneOf Directive
The @oneOf directive enforces that exactly one field of an input object is non-null. This is useful for
union-like inputs where callers choose one of several ways to identify or filter an entity.
Basic usage
input CharacterSearchInput @oneOf {
byName: String
byBirthYear: String
byId: ID
}
type Query {
searchCharacter(search: CharacterSearchInput!): Character
}
If the client provides zero or more than one field, the request is rejected during validation.
Combining @oneOf with @idOf
When one of the alternatives is a Global ID, add @idOf to make the ID typed and to enable automatic decoding.
input CharacterSearchInput @oneOf @scope(to: ["default"]) {
"""
Search by character name
"""
byName: String
# tag::id_example[4] ID search sample
"""
Search by character ID
"""
byId: ID @idOf(type: "Character")
"""
Search by birth year
"""
byBirthYear: String
}
Client query examples:
# Option A: by name
query {
searchCharacter(search: { byName: "Luke Skywalker" }) {
id
name
}
}
# Option B: by Global ID (typed via @idOf)
query {
searchCharacter(search: { byId: "Q2hhcmFjdGVyOjE=" }) {
id
name
}
}
With @idOf, Viaduct will validate that the ID is a Character Global ID and decode it before your resolver runs.
Resolver pattern
Inside the resolver, inspect which field was set and branch accordingly. When the ID path is used, the internal ID is already validated and decoded by Viaduct.
@Resolver
class SearchCharacterQueryResolver : QueryResolvers.SearchCharacter() {
override suspend fun resolve(ctx: Context): viaduct.api.grts.Character? {
val search = ctx.arguments.search
val byName = search.byName
val byId = search.byId
val byBirthYear = search.byBirthYear
val character = when {
byName != null -> CharacterRepository.findCharactersByName(byName).firstOrNull()
byId != null -> CharacterRepository.findById(byId.internalID)
byBirthYear != null -> CharacterRepository.findCharactersByYearOfBirth(byBirthYear).firstOrNull()
else -> null
}
return character?.let {
CharacterBuilder(ctx).build(character)
}
}
Error behavior
- Zero fields provided: validation error (input must include exactly one field).
- Multiple fields provided: validation error.
- Wrong ID type: validation error before the resolver runs (thanks to
@idOf).
Design guidelines
- Keep input alternatives orthogonal (avoid overlapping semantics).
- Use
@idOfon the ID branch so the resolver receives a typed and decoded internal ID. - Return
nullfor “not found” results; keep exceptions for unexpected failures.
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.