Skip to main content

mutationUpdate

Overview

mutationUpdate is analogous to update option passed to mutate method in Apollo.
It is used to update the cache following the execution of a mutation.

Usage

As stated previously in the mutations guide, unlike UpdateBookMutation, updating the cache after executing AddBookMutation requires manual intervention.

Let's build on the same example used in fetching guide to demonstrate how mutationUpdate can be used to update the cache after executing AddBookMutation.

In this example, we have a library management app. A book is present in two lists stored in the cache, one for all books in the library and one for books written by an author.

First we add the mutation to book.graphql

library/gql/book.graphql
mutation AddBook($book: BookInput!) {
addBook(book: $book) {
...BookFragment
}
}

Then, we define two state slices, one for managing book state and one for author state and add them to Apollo Orbit module as demonstrated previously.

bookState

library/states/book.state.ts
import { state } from '@apollo-orbit/angular';
import { AddBookMutation, BooksQuery } from '../../graphql/types';

export const bookState = state(descriptor => descriptor
.mutationUpdate(AddBookMutation, (cache, info) => {
const addBook = info.data?.addBook;
if (!addBook) return;

cache.updateQuery(new BooksQuery(), data => data ? { books: [...data.books, addBook] } : data);
})
);

authorState

library/states/author.state.ts
import { identifyFragment, state } from '@apollo-orbit/angular';
import { AddBookMutation, AuthorFragmentDoc } from '../../graphql/types';

export const authorState = state(descriptor => descriptor
.mutationUpdate(AddBookMutation, (cache, info) => {
const addBook = info.data?.addBook;
if (!addBook) return;

const authorId = info.variables?.book.authorId as string;
cache.updateFragment(
identifyFragment(AuthorFragmentDoc, authorId),
author => author ? ({ ...author, books: [...author.books, addBook] }) : author
);
})
);

info argument is of type MutationInfo<AddBookMutationData, AddBookMutationVariables> and all logic in mutationUpdate handler is type-safe.

identifyFragment is a helper function provided by Apollo Orbit for returning a fragment object that uniquely identifies a fragment in the cache.

Execute mutation

library/books.component.ts
export class BooksComponent {
public constructor(
private readonly apollo: Apollo,
) { }

protected addBook(book: BookInput): void {
this.apollo.mutate(new AddBookMutation({ book })).subscribe();
this.apollo.mutate({
...new AddBookMutation({ book }),
update(cache, result) {
const addBook = result.data?.addBook;
if (!addBook) return;

// Update full list of books
cache.updateQuery(new BooksQuery(), data => data ? { books: [...data.books, addBook] } : data);

// Update author's list of books
cache.updateFragment(
identifyFragment(AuthorFragmentDoc, book.authorId),
author => author ? ({ ...author, books: [...author.books, addBook] }) : author
);
}
}).subscribe();
}
}

Now, when a component calls this.apollo.mutate(new AddBookMutation({ book })).subscribe() the above mutation update functions are automatically executed and the UI displaying books and author books is updated.

The example above demonstrates how the same mutation can be handled independently by different states, achieving separation of concerns (SoC) and complete decoupling between component and state logic.

note

mutationUpdate also accepts the name of the mutation as a string argument. This can be used in projects that do not have codegen setup.
So the above code can be updated to .mutationUpdate('AddBook', (cache, info) => and the rest of the code remains the same, except it won't be type-safe.