Mutations
In addition to fetching data using queries, Apollo iOS also handles GraphQL mutations. Mutations are identical to queries in syntax, the only difference being that you use the keyword mutation
instead of query
to indicate that the root fields on this query are going to be performing writes to the backend.
For more information on GraphQL mutations, we recommend reading this guide.
GraphQL mutations represent two things in one operation:
The mutation field name with arguments, which represents the actual operation to be done on the server.
The fields you want back from the result of the mutation to update the client.
All business logic involved in mutating data is handled by the server. The client has no direct knowledge of how data will be mutated. Just like any other field, each mutation in a schema returns a type. If that type is an object type, you may query fields on that type, which can be used to fetch the new state of the mutated object.
In this example, we define a mutation called UpvotePost
, which performs the schema's upvotePost(postId:)
mutation.
1mutation UpvotePost($postId: Int!) {
2 upvotePost(postId: $postId) {
3 id
4 votes
5 }
6}
The server implements the upvotePost(postId:)
mutation to add an upvote to the post with the given postId
and return that post. The above mutation selects the id
and votes
fields on the returned Post
object.
The result might be:
1{
2 "data": {
3 "upvotePost": {
4 "id": "123",
5 "votes": 5
6 }
7 }
8}
Performing mutations
Similar to queries, mutations are represented by instances of generated classes, conforming to the GraphQLMutation
protocol. Operation arguments are generated used to define mutation variables. For more information on passing arguments to a mutation see "Operation arguments"
You pass a mutation object to ApolloClient.perform(mutation:)
to send the mutation to the server, execute it, and receive typed results.
1Task {
2 do {
3 let response = try await apollo.perform(mutation: UpvotePostMutation(postId: postId))
4 print(response.data?.upvotePost?.votes)
5 } catch {
6 print("Error performing mutation: \(error)")
7 }
8}
Using fragments in mutation results
In many cases, you'll want to use mutation results to update your UI. Fragments can be a great way of sharing result handling between queries and mutations:
1mutation UpvotePost($postId: Int!) {
2 upvotePost(postId: $postId) {
3 ...PostDetails
4 }
5}
1Task {
2 do {
3 let response = try await client.perform(mutation: UpvotePostMutation(postId: postId))
4 self.configure(with: response.data?.upvotePost?.fragments.postDetails)
5 } catch {
6 print("Error performing mutation: \(error)")
7 }
8}
Remember to dispatch your UI updates on the MainActor
! ApolloClient
will be called async
, but will return a response to the async
context where it is called.
Passing input objects
The GraphQL type system includes input objects as a way to pass complex values to fields. Input objects are often defined as mutation variables, because they give you a compact way to pass in objects to be created:
1mutation CreateReviewForEpisode($episode: Episode!, $review: ReviewInput!) {
2 createReview(episode: $episode, review: $review) {
3 stars
4 commentary
5 }
6}
1let review = ReviewInput(stars: 5, commentary: "This is a great movie!")
2Task {
3 do {
4 let response = try await apollo.perform(mutation: CreateReviewForEpisodeMutation(episode: .jedi, review: review))
5 // Handle response as needed
6 } catch {
7 print("Error creating review: \(error)")
8 }
9}