Skip to main content

Mutations

Create a mutation

Unlike queries, mutations are typically used to create/update/delete data or perform server side-effects. You can use MutationBuilder or useMutation to create a mutation.

  MutationBuilder<Map<String, dynamic>, dynamic, Map<String, dynamic>, dynamic>(
'sign-up',
(variables) {
return Future.delayed(
const Duration(seconds: 5),
() => {
'name': variables['name'],
'email': variables['email'],
'password': variables['password'],
},
);
},
builder: (context, mutation) {
/* ... */
},
);

MutationBuilder or useMutation requires 4 type arguments to provide proper type intellisense and type-safety. This might seem overwhelming but at the end of the day saves time and effort. The type arguments are as follows in order:

  1. DataType - The type of the data returned by the mutation function
  2. ErrorType - The type of the error returned by the mutation function
  3. VariablesType - The type of the variables passed to the mutation function
  4. RecoveryType - The type of the data returned by the onMutate callback and to be passed on onSuccess & onError callback. Which can be used to recover from an error and continue the mutation flow.

Callbacks

MutationBuilder or useMutation comes with some helper parameters that allow quick and easy side-effects at any stage during the mutation lifecycle. These come in handy for both invalidating and refetching queries after mutations and even optimistic updates

MutationBuilder<Map<String, dynamic>, dynamic, Map<String, dynamic>, String>(
'sign-up',
(variables) {
return Future.delayed(
const Duration(seconds: 5),
() => {
'name': variables['name'],
'email': variables['email'],
'password': variables['password'],
},
);
},
onMutate: (variables) {
print('onMutate: $variables');
return "Recover ME";
},
onData: (data, recoveryData) {
print('onData: $data');
print('recoveryData: $recoveryData');
},
onError: (error, recoveryData) {
print('onError: $error');
print('recoveryData: $recoveryData');
},
builder: (context, mutation) {
/* ... */
},
);

Learn how to use onMutate & Query.setData to implement optimistic updates

Refetch Queries and InfiniteQueries on successful mutation

MutationBuilder<Map<String, dynamic>, dynamic, Map<String, dynamic>, dynamic>(
'sign-up',
/* ... */,
refreshQueries: const ['user-profile'],
refreshInfiniteQueries: const ['feeds'],
)

Mutation

The MutationBuilder/useMutation returns a Mutation object that can be used to trigger the mutation and access the state of the mutation.

States

A mutation can only be in one of the following states at any given moment:

  • isInactive - The mutation is currently idle or in a fresh/reset state
  • isMutating - The mutation is currently running and performing the mutation

Beyond those primary states, more information is available depending on the state of the mutation:

  • hasError - If the mutation is in an error state, the error is available via the error property.
  • hasData - If the mutation is in a success state, the data is available via the data property.

Performing a mutation

You can use the Mutation.mutate method to trigger your mutation. The mutate method accepts a single variable or object as an argument. This variable or object will be passed to your mutation function.

await mutation.mutate({
'name': 'John Doe',
'email': '[email protected]'
'password': 'password',
})

The variables must be the type specified as VariableType in MutationBuilder or useMutation.

All mutations are by default asynchronous and immediately returns a Future with available data. But if you want to schedule the mutation in queue and wait for the result, you need to pass the scheduleToQueue: true parameter to the mutate method

await mutation.mutate(
{
'name': 'John Doe',
'email': '[email protected]'
'password': 'password',
},
scheduleToQueue: true,
)

Resetting a mutation

You can use the Mutation.reset method to reset the mutation to its initial state

await mutation.reset();

Dynamic key

You can use Dart's string interpolation to dynamically generate the key for a mutation. This is useful when you want separate mutations that has same data type but are triggered by different events.

MutationBuilder<Map<String, dynamic>, dynamic, Map<String, dynamic>, dynamic>(
'sign-up?provider=$authProvider',
(variable) => auth.signUp(variable, provider: authProvider),
builder: (context, mutation) {
/* ... */
},
)

For every authProvider a new mutation will be created and will be cached separately. Which are also isolated from each other.

Btw, authProvider and auth.signUp are imaginary variables and methods