Join us for GraphQL Summit, October 10-12 in San Diego. Use promo code ODYSSEY for $400 off your pass.
Docs
Launch GraphOS Studio
You're viewing documentation for a previous version of this software. Switch to the latest stable version.

Mutations


Queries are useful to fetch data from a server, but client-server communication may also require sending data to the server. This is where s become handy. Just like REST, any request might end up causing some side-effects on the server, but by convention it's suggested that one doesn't use GET requests to modify data. GraphQL is similar - technically any query could be implemented to cause a data write. However, it's useful to establish a convention that any s that cause writes should be sent explicitly via a mutation.

Apollo Android handles GraphQL s. Mutations are similar to queries in syntax, the only difference being that you use the keyword mutation instead of query to indicate that the root s on this query are going to be performing writes to the backend.

mutation UpvotePost($postId: Int!) {
upvotePost(postId: $postId) {
votes
}
}

GraphQL s represent two things in one query string:

  1. The name with s, upvotePost, which represents the actual to be done on the server
  2. The s you want back from the result of the to update the client: { votes }

The above will upvote a post on the server. The result might be:

{
"data": {
"upvotePost": {
"id": "123",
"votes": 5
}
}
}

Similar to queries, s are represented by instances of generated classes, conforming to the ApolloMutationCall interface. Constructor s are used to define s. You pass a mutation object to ApolloClient#perform(mutation) to send the to the server, execute it, and receive typed results:

val upvotePostMutation = UpvotePostMutation(votes = 3)
apolloClient
.mutate(upvotePostMutation)
.enqueue(object: ApolloCall.Callback<UpvotePost.Data>() {
override fun onResponse(response: Response<UpvotePost.Data>) {
Log.i(TAG, response.toString());
}
override fun onFailure(e: ApolloException) {
Log.e(TAG, e.getMessage(), e);
}
}
)
UpvotePostMutation upvotePostMutation = UpvotePostMutation.builder()
.votes(3)
.build();
apolloClient
.mutate(upvotePostMutation)
.enqueue(
new ApolloCallback<>(new ApolloCall.Callback<UpvotePost.Data>() {
@Override public void onResponse(@NotNull Response<UpvotePost.Data> response) {
Log.i(TAG, response.toString());
}
@Override public void onFailure(@NotNull ApolloException e) {
Log.e(TAG, e.getMessage(), e);
}
});
);

Using fragments in mutation results

In many cases, you'll want to use results to update your UI. s can be a great way of sharing result handling between queries and mutations:

mutation UpvotePost($postId: Int!) {
upvotePost(postId: $postId) {
...PostDetails
}
}
apolloClient
.mutate(upvotePostMutation)
.enqueue(object: ApolloCall.Callback<UpvotePost.Data>() {
override fun onFailure(e: ApolloException) {
Log.e(TAG, e.getMessage(), e);
}
override fun onResponse(response: Response<UpvotePost.Data>) {
Log.i(TAG, response.data.upvotePost.fragments.postDetails);
}
}
)
apolloClient
.mutate(upvotePostMutation)
.enqueue(
new ApolloCallback<>(new ApolloCall.Callback<UpvotePost.Data>() {
@Override public void onResponse(@NotNull Response<UpvotePost.Data> response) {
Log.i(TAG, response.data.upvotePost.fragments.postDetails);
}
@Override public void onFailure(@NotNull ApolloException e) {
Log.e(TAG, e.getMessage(), e);
}
})
);

Passing input objects

The GraphQL type system includes input objects as a way to pass complex values to s. Input objects are often defined as s, because they give you a compact way to pass in objects to be created:

mutation CreateReviewForEpisode($episode: Episode!, $review: ReviewInput!) {
createReview(episode: $episode, review: $review) {
stars
commentary
}
}
val reviewInput = ReviewInput(stars = 5, commentary = "This is a great movie!")
apolloClient.mutate(CreateReviewForEpisodeMutation(episode = Episode.NEWHOPE, review = reviewInput))
ReviewInput reviewInput = ReviewInput.Builder()
.stars(5)
.commentary("This is a great movie!")
.build()
apolloClient.mutate(CreateReviewForEpisodeMutation(Episode.NEWHOPE, reviewInput))

Designing mutation results

In GraphQL, s 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 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.

Uploading files

Apollo Android supports file uploading over graphql-multipart-request-spec.

You need to define this mapping in your build.gradle file.

apollo {
customTypeMapping = [
"Upload" : "com.apollographql.apollo.api.FileUpload"
]
}

Note1 You don't need to register custom type adapter for FileUpload.

Note2 While Apollo Android doesn't use reflection for regular s, it does for file upload. If you're using Proguard/R8, you need to keep everything inheriting from InputType:

proguard-rules.pro
-keep class * implements com.apollographql.apollo.api.InputType { *; }

In this example, the GraphQL uses custom type named Upload for file upload. Change it to match your GraphQL .

Create graphql .

mutation SingleUpload($file: Upload!) {
singleUpload(file: $file) {
id
path
filename
mimetype
}
}

Call your with mimetype and a valid file path.

mutationSingle = SingleUploadMutation(file = FileUpload.create("image/jpeg", "/my/image.jpg"))
mutationSingle = SingleUploadMutation.builder()
.file(FileuploadKt.create(FileUpload.Companion, "image/jpeg", "/my/image.jpg"))
.build();

If you don't have a File, you can also subclass FileUpload:

object upload : FileUpload(mimetype) {
override fun contentLength(): Long {
TODO("return contentLength here")
}
override fun fileName(): String? {
TODO("return fileName to use in the multipart request here")
}
override fun writeTo(sink: BufferedSink) {
TODO("write the data here")
}
}
FileUpload upload = new FileUpload(mimetype) {
@Override
public long contentLength() {
// return contentLength here
}
@Override
public String fileName() {
// return fileName to use in the multipart request here
}
@Override
public void writeTo(@NonNull BufferedSink sink) {
// write the data here
}
};
Previous
Queries
Next
Normalized cache
Edit on GitHubEditForumsDiscord