Synapsev6.0.0

useApiMutation — React hook for mutations

TL;DR: useApiMutation(endpoint, options?) — a write hook (POST/PUT/DELETE/PATCH). It doesn't start on its own; you run it via mutate (fire-and-forget) or mutateAsync (await + rethrows the error). On success the endpoint's invalidatesTags auto-refetch the related useApiQuery.

A React hook over an ApiClient endpoint for writes (POST/PUT/DELETE/PATCH). Unlike useApiQuery, the request does not start automatically — you trigger it with mutate/mutateAsync. Mutations aren't cached (by REST method), and their invalidatesTags invalidate the cache — active useApiQuery hooks of neighbouring endpoints refetch on their own via the invalidation bus.

When to use it / when you don't need it

  • Use it for any write from a React component, when you want isLoading/isError states for a button and automatic invalidation of related queries after success.

  • Not needed for reads (GET) — that's useApiQuery.

  • Not needed outside React or in effects — call endpoint.request(...) directly (see ApiClient).

Import

typescript
import { useApiMutation } from 'synapse-storage/react'

Usage

typescript
const endpoints = pokemonApiClient.getEndpoints()

function CreatePokemon() {
  const { mutate, isLoading, isError, error } = useApiMutation(endpoints.createPokemon)

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault()
        mutate({ name: 'Pikachu' }) // fire-and-forget
      }}
    >
      <button disabled={isLoading}>Create</button>
      {isError && <Error message={error?.message} />}
    </form>
  )
}

Return value

useApiMutation(endpoint, options?) returns:

FieldTypeDescription
mutate(params) => voidRun the mutation; errors are not thrown (read error/isError)
mutateAsync(params) => Promise<QueryResult>Run and await; rejects on error
dataTData | undefinedData of a successful response
errorError | undefinedMutation error
status'idle' | 'loading' | 'success' | 'error'Current status
isLoadingbooleanstatus === 'loading'
isErrorbooleanstatus === 'error'
isSuccessbooleanstatus === 'success'
reset() => voidReset state back to idle

options is the endpoint's QueryOptions (a mutation has no enabled/refetchOnInvalidate, since it's triggered manually):

typescript
useApiMutation(endpoints.createPokemon, {
  timeout: 8000,             // mutation timeout (ms)
  signal: controller.signal, // external cancellation (the hook already cancels on unmount)
  headers: new Headers(),    // extra headers
  context: { source: 'ui' }, // passed into baseQuery.prepareHeaders
  retry: { count: 1 },       // retries for this mutation
  // disableCache is irrelevant here: mutations are never cached anyway
})

mutate vs mutateAsync

  • mutate(params) — fire-and-forget. The rejection is swallowed (state already reflects the error), so you don't need a .catch. Best for simple form submits.

  • mutateAsync(params) — returns the promise and rethrows on error, so you can await and branch in flow:

Notes

  • StrictMode-safe. On unmount the in-flight request is aborted and state updates are skipped.

  • Mutations are never written to the cache (only GET is cached), so there's no fromCache here.

See also

  • useApiQuery — the companion hook for reads.

  • ApiClient — caching, tags and the invalidation bus.