Skip to main content

signal.mutation

Execute a GraphQL mutation and track its state using reactive Signals.

API

Apollo.signal.mutation<TData, TVariables, TErrorPolicy>(
mutation: TypedDocumentNode<TData, TVariables>,
options?: SignalMutationOptions<TData, TVariables, TErrorPolicy>
): SignalMutation<TData, TVariables, TErrorPolicy>

Accepts a required GraphQL mutation document and an optional SignalMutationOptions object.

Returns a SignalMutation<TData, TVariables> instance. This object provides several reactive Signals (result, data, loading, error, called) that reflect the state of the mutation execution. It also includes methods for interaction, primarily mutate and reset.

Options
PropertyTypeDescription
optimisticResponse?Unmasked<NoInfer<TData>> | ((vars: TVariables, { IGNORE }: { IGNORE: IgnoreModifier }) => Unmasked<NoInfer<TData>> | IgnoreModifier)By providing either an object or a callback function that, when invoked after
a mutation, allows you to return optimistic data and optionally skip updates
via the IGNORE sentinel object, Apollo Client caches this temporary
(and potentially incorrect) response until the mutation completes, enabling
more responsive UI updates.

For more information, see Optimistic mutation results.
updateQueries?MutationQueryReducersMap<TData>A MutationQueryReducersMap, which is map from query names to
mutation query reducers. Briefly, this map defines how to incorporate the
results of the mutation into the results of queries that are currently
being watched by your application.
refetchQueries?((result: NormalizedExecutionResult<Unmasked<TData>>) => InternalRefetchQueriesInclude) | InternalRefetchQueriesIncludeAn array (or a function that returns an array) that specifies which queries you want to refetch after the mutation occurs.

Each array value can be either:

- An object containing the query to execute, along with any variables

- A string indicating the operation name of the query to refetch
awaitRefetchQueries?booleanIf true, makes sure all queries included in refetchQueries are completed before the mutation is considered complete.

The default value is false (queries are refetched asynchronously).
update?MutationUpdaterFunction<TData, TVariables, TCache>A function used to update the Apollo Client cache after the mutation completes.

For more information, see Updating the cache after a mutation.
onQueryUpdated?OnQueryUpdated<any>Optional callback for intercepting queries whose cache data has been updated by the mutation, as well as any queries specified in the refetchQueries: [...] list passed to client.mutate.

Returning a Promise from onQueryUpdated will cause the final mutation Promise to await the returned Promise. Returning false causes the query to be ignored.
context?DefaultContextIf you're using Apollo Link, this object is the initial value of the context object that's passed along your link chain.
fetchPolicy?MutationFetchPolicyProvide no-cache if the mutation's result should not be written to the Apollo Client cache.

The default value is network-only (which means the result is written to the cache).

Unlike queries, mutations do not support fetch policies besides network-only and no-cache.
keepRootFields?booleanTo avoid retaining sensitive information from mutation root field
arguments, Apollo Client v3.4+ automatically clears any ROOT_MUTATION
fields from the cache after each mutation finishes. If you need this
information to remain in the cache, you can prevent the removal by passing
keepRootFields: true to the mutation. ROOT_MUTATION result data are
also passed to the mutation update function, so we recommend obtaining
the results that way, rather than using this option, if possible.
onCompleted?(data: TData, options: MutationOptions<TData, TVariables>) => voidA callback function that's called when your mutation successfully completes with zero errors (or if
errorPolicy is ignore and partial data is returned).

This function is passed the mutation's result data and the options it was executed with.
onError?(error: ErrorLike, options: MutationOptions<TData, TVariables>) => voidA callback function that's called when the mutation encounters one or more errors (unless
errorPolicy is ignore).

This function is passed the error and the options the mutation was executed with. It is a side effect
only: it does not stop the promise mutate returned from rejecting under an errorPolicy of none.
errorPolicy?TErrorPolicySpecifies how the mutation handles a response that returns both GraphQL errors and partial results.

For details, see GraphQL error policies.

The default value is none, meaning that the mutation result includes error details but not partial results.

Declared here because it types the result signals.
Signals
SignalTypeDescription
resultSignal<SignalMutationResult<TData, TErrorPolicy>>The mutation result, containing data, loading, error and called.
loadingSignal<boolean>If true, the mutation is currently in flight.
dataSignal<TData | undefined>The data returned from the mutation.
errorSignal<EffectiveMutateErrorPolicy<TErrorPolicy> extends 'ignore' ? undefined : ErrorLike | undefined>The error encountered during the mutation.
calledSignal<boolean>If true, the mutation's mutate method has been called.
Methods
MethodDescription
mutate(executeOptions?: SignalMutationExecutionOptions<TData, TVariables>)Execute the mutation with the provided variables and options.
reset()Reset the mutation result to its initial state.

Executing a mutation

To execute a mutation, inject Apollo and call signal.mutation with the GraphQL mutation document. Then, call the mutate method on the returned SignalMutation instance, passing execution options (like variables).

When the mutation has required GraphQL variables, mutate() requires execution options containing them. Otherwise, its options can be omitted.

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

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

Saving book.graphql will trigger codegen of UPDATE_BOOK_MUTATION typed gql document in /graphql/operations.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, define the mutation in your component:

library/books/books.component.ts
import { Apollo, toErrorLike } from '@apollo-orbit/angular';
import { UPDATE_BOOK_MUTATION, UpdateBookInput } from '../graphql';

@Component({
selector: 'app-books',
templateUrl: './books.component.html'
})
export class BooksComponent {
private readonly apollo = inject(Apollo);
private readonly notificationService = inject(NotificationService);

protected readonly updateBookMutation = this.apollo.signal.mutation(UPDATE_BOOK_MUTATION);

protected async updateBook(id: string, book: UpdateBookInput): Promise<void> {
try {
const { data } = await this.updateBookMutation.mutate({ variables: { id, book } });
this.notificationService.success(`Book '${data.updateBook.name}' was updated successfully.`);
} catch (error) {
this.notificationService.error(`Failed to update book '${book.name}': ${toErrorLike(error).message}`);
}
}
}
info

The promise follows the operation's errorPolicy: it rejects under none (the default), and resolves under all and ignore. Under none, a fulfilled promise has non-optional data. Failures are also available through the operation's error() signal.

Ignoring the promise is safe: the latest execution reaches the signals, and Orbit marks the promise so an unhandled rejection is never raised. Chaining .then, .catch or .finally creates a new promise that needs its own rejection handling.

Finally, use the signals in the template to provide feedback:

library/books/books.component.ts
<h3>Books</h3>

@if (updateBookMutation.called()) { <!-- Optional check -->
@if (updateBookMutation.error(); as error) {
<div class="error">Error: {{ error.message }}</div>
} @else if (updateBookMutation.data(); as data) {
<div class="success">Book '{{ data.updateBook.name }}' was updated successfully.</div>
}
}

...

<button (click)="updateBook(...)" [disabled]="updateBookMutation.loading()">
{{ updateBookMutation.loading() ? 'Updating...' : 'Update' }}
</button>
note

Compared to the observable-based mutate method, signal.mutation neatly encapsulates the mutation state into a single SignalMutation instance, removing the need for tracking state in separate loading and error signals as shown in the mutate example.

Callbacks and overlapping executions

onCompleted handles successful data; onError handles errors without preventing promise rejection. Set errorPolicy on signal.mutation; mutate() cannot override it.

Only the latest call updates the signals; earlier calls still settle and run callbacks. reset() clears the signals and prevents pending calls from updating them, without cancelling requests or callbacks.