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.
1apollo.perform(mutation: UpvotePostMutation(postId: postId)) { result in
2 guard let data = try? result.get().data else { return }
3 print(data.upvotePost?.votes)
4}
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}
1client.perform(mutation: UpvotePostMutation(postId: postId)) { result in
2 guard let data = try? result.get().data else { return }
3 self.configure(with: data.upvotePost?.fragments.postDetails)
4}
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!")
2apollo.perform(mutation: CreateReviewForEpisodeMutation(episode: .jedi, review: review))
Designing mutation results
When people talk about GraphQL, they often focus on the data fetching side of things, because that's where GraphQL brings the most value. Mutations can be pretty nice if done well, but the principles of designing good mutations, and especially good mutation result types, are not yet well-understood in the open source community. So when you are working with mutations it might often feel like you need to make a lot of application-specific decisions.
In GraphQL, mutations can return any type, and that type can be queried just like a regular GraphQL query. So the question is - what type should a particular mutation return?
In most cases, the data available from a mutation result should be the server developer's best guess of the data a client would need to understand what happened on the server. For example, a mutation that creates a new comment on a blog post might return the comment itself. A mutation that reorders an array might need to return the whole array.