Skip to main content

Mutations

Prerequisites

This article assumes you're familiar with building basic GraphQL mutations. If you need a refresher, we recommend this guide.

This article also assumes that you've already set up your environment as per getting started guide.

Overview

mutate is the primary method for modifying GraphQL back-end data in Apollo Orbit.

For a full documentation of mutations, please refer to the Apollo Client docs

mutate

mutate<TData, TVariables>(options: MutationOptions<TData, TVariables>): Observable<MutationResult<TData>>

Returns a MutationResult<TData, TVariables> observable. This observable terminates with a result when the mutation is successful or an an error when it it fails.

note

The observable must be subscribed to for the mutation to execute.

Executing a mutation

To execute a mutation, inject Apollo and pass it a GraphQL mutation document.

Let's look at an example.

First, we'll create a GraphQL mutation named UpdateBook:

library/gql/book.graphql
mutation UpdateBook($id: ID!, $book: BookInput!) {
updateBook(id: $id, book: $book) {
...BookFragment
}
}

Saving book.graphql will trigger codegen of UpdateBookMutation class in /graphql/types.ts file as per our setup.

For more information about codegen and how it works, please refer to the Codegen section.

tip

We have used the same BookFragment from our previous query example, that way Apollo Client will automatically normalize and update the cache with the new data returned from the mutation.
This will automatically update any active QueryObservable instances that are watching the same data, and the UI will display the new data without any additional effort by the developer.

Next, we execute the mutation and handle the result.

library/books/books.component.ts
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { Apollo } from '@apollo-orbit/angular';
import { UpdateBookMutation } from '../graphql/types';

@Component({
selector: 'app-books',
templateUrl: './books.component.html',
styleUrls: ['./books.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class BooksComponent {
protected readonly error = signal<Error | undefined>(undefined);

public constructor(
private readonly apollo: Apollo,
private readonly notificationService: NotificationService
) { }

...

protected updateBook(id: string, book: BookInput): void {
this.error.set(undefined);
this.apollo.mutate(new UpdateBookMutation({ id, book })).subscribe({
next: result => this.notificationService.success(`Book '${result.data!.updateBook.name}' was updated successfully.`),
error: (error) => this.error.set(error)
});
}
}
note

We apply non-null assertion ! to result.data above because we know that, in this case, a successful mutation will always return data.
In some edge cases both data and error might be undefined, for example when errorPolicy is set to ignore.

mapMutation

Similar to mapQuery, Apollo Orbit provides a mapMutation RxJS operator to map the data of a mutation result while preserving the other properties.
This is useful when you want to map the mutation result without dealing with the nullability of data property.

import { mapMutation } from '@apollo-orbit/angular';
...
this.apollo.mutate(new UpdateBookMutation({ id, book })).pipe(
mapMutation(data => data.updateBook.name)
).subscribe({
next: name => this.notification.success(`Book '${name}' was updated successfully.`),
error: (error) => this.error.set(error)
});

Cache normalisation

As we've seen above, Apollo Client automatically updates the cache with the new data returned from the mutation.
But this might not be as straight forward with other mutations like addBook or deleteBook where the cache needs to be updated manually because Apollo Client cannot identify which parts of the cache reference the list of books.
For example, we might need to add a book to an author's books list and to the full list of books in cache.

This, including optimistic UI & refetching queries, will be covered in more detail in the State section of the docs.