Mutations
Implementing mutation operations in the Star Wars demo app using Viaduct.
The Star Wars demo app includes several mutation operations that allow you to modify data. All mutations are available
under the Mutation root type and demonstrate how to implement data modification operations in Viaduct.
Mutation implementation patterns
Mutations in Viaduct follow similar patterns to queries but focus on data modification operations. Each mutation resolver typically:
- Validates input data using input types with appropriate constraints.
- Performs the data modification on the underlying data store.
- Returns updated entities that can be further resolved with additional fields.
- Maintains data consistency and referential integrity.
Available mutations
Create a new character
input CreateCharacterInput @scope(to: ["default"]) {
name: String!
birthYear: String
eyeColor: String
gender: String
hairColor: String
height: Int
mass: Float
homeworldId: ID @idOf(type: "Planet")
speciesId: ID @idOf(type: "Species")
}
"""
Create a new Character.
"""
createCharacter(input: CreateCharacterInput!): Character @resolver
Implementation:
@Resolver
class CreateCharacterMutation : MutationResolvers.CreateCharacter() {
override suspend fun resolve(ctx: Context): Character {
val input = ctx.arguments.input
val homeworldId = input.homeworldId
val speciesId = input.speciesId
// TODO: Validate homeworld and species are valid ids
// Create and store the new character
val character = CharacterRepository.add(
com.example.starwars.modules.filmography.characters.models.Character(
id = "",
name = input.name,
birthYear = input.birthYear,
eyeColor = input.eyeColor,
gender = input.gender,
hairColor = input.hairColor,
height = input.height,
mass = input.mass?.toFloat(),
homeworldId = homeworldId?.internalID,
speciesId = speciesId?.internalID,
)
)
// Build and return the GraphQL Character object from the created entity
return CharacterBuilder(ctx).build(character)
}
}
Execution :
mutation {
createCharacter(input: {
name: "New Jedi"
birthYear: "19BBY"
eyeColor: "blue"
gender: "male"
hairColor: "brown"
height: 180
mass: 75.5
homeworldId: "UGxhbmV0OjE=" # Tatooine
speciesId: "U3BlY2llczox" # Human
}) {
id
name
birthYear
homeworld { name }
species { name }
}
}
Implementation notes:
- Uses input types for structured data validation.
- Generates new GlobalIDs for created entities.
- Supports relationship creation via reference IDs.
- Returns the full created entity for immediate use.
Update character name
"""
Update an existing Character's name.
"""
updateCharacterName(id: ID! @idOf(type: "Character"), name: String!): Character @resolver
Implementation notes:
@Resolver
class UpdateCharacterNameMutation : MutationResolvers.UpdateCharacterName() {
override suspend fun resolve(ctx: Context): Character? {
val id = ctx.arguments.id
val name = ctx.arguments.name
// Fetch existing character
val character = CharacterRepository.findById(id.internalID)
?: throw IllegalArgumentException("Character with ID ${id.internalID} not found")
// Update character's name
val updatedCharacter = character.copy(name = name)
val newCharacter = CharacterRepository.update(updatedCharacter)
// Return the updated character as a GraphQL object
return CharacterBuilder(ctx).build(newCharacter)
}
}
- Uses GlobalIDs for entity identification.
- Performs atomic field updates.
- Returns updated entity for verification.
Add character to film
"""
Link a Character to a Film (adds the character to that film's cast).
Allows locating the character either directly by ID or via the existing search input.
"""
addCharacterToFilm(input: AddCharacterToFilmInput!): AddCharacterToFilmPayload @resolver
}
mutation {
addCharacterToFilm(input: {
filmId: "RmlsbTox" # A New Hope
characterId: "Q2hhcmFjdGVyOjU=" # Obi-Wan Kenobi
}) {
character {
name
}
film {
title
}
}
}
Implementation notes:
- Manages many-to-many relationships.
- Uses input types for relationship data.
- Returns both related entities for verification.
- Maintains bidirectional relationship consistency.
Delete character
"""
Delete a Character by ID. Returns true if a record was removed.
"""
deleteCharacter(id: ID! @idOf(type: "Character")): Boolean @resolver
Implementation notes:
- Uses GlobalIDs for entity identification.
- Returns boolean success indicator.
- Handles cascading relationship cleanup.
- Maintains data integrity during deletion.
Mutation best practices
- Use input types: structure mutation arguments with dedicated input types for validation and clarity.
- GlobalID consistency: always use encoded GlobalIDs for entity references.
- Return useful data: return updated entities or relationship objects, not just success flags.
- Validate relationships: ensure referenced entities exist before creating relationships.
- Handle errors gracefully: provide meaningful error messages for invalid operations.
- Maintain consistency: update all related data structures atomically.
Note: when using mutations, make sure to use properly encoded GlobalIDs.
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.