Skip to content

Mutations ​

Mutations are API/server writes with optimistic cache updates, rollback, invalidation, and refetch. They complement resources (which handle reads).

Define a Mutation ​

ts
const createProduct = mesh.mutation("products.create", {
  optimistic(_state, input: { name: string }, ctx) {
    ctx.setResourceData(productsResource, { search: "", page: 1 }, (current) => [
      { id: "temp", name: input.name, optimistic: true },
      ...(current ?? [])
    ]);
  },
  async mutate(input) {
    return api.post<Product>("/products", input);
  },
  invalidate: [{ type: "products" }],
  refetch: "active"
});
OptionTypeDescription
optimistic(state, input, ctx) => voidUpdate state/cache optimistically before the API call
mutate(input) => Promise<T>The async write operation
invalidateTag[]Tags to invalidate after success
refetch"active" | "all" | string[]Which invalidated resources to refetch
rollbackbooleanRollback optimistic update on failure (default: true when optimistic is set)
offlinebooleanQueue the mutation when offline and flush on reconnect

Use in React ​

tsx
import { useMeshMutation } from "statemesh-core";

function NewProductButton() {
  const create = useMeshMutation(createProduct);

  return (
    <button
      disabled={create.pending}
      onClick={() => void create.run({ name: "Keyboard" })}
    >
      {create.pending ? "Creating..." : "Create Product"}
    </button>
  );
}

useMeshMutation Return ​

FieldTypeDescription
run(input) => Promise<T>Execute the mutation
pendingbooleanTrue while the mutation is in-flight
errorError | undefinedThe last mutation error
reset() => voidClear error state

Optimistic Updates ​

The optimistic function runs before the API call. Use ctx.setResourceData to update the resource cache optimistically:

ts
optimistic(state, input, ctx) {
  ctx.setResourceData(productsResource, { search: "", page: 1 }, (current) => [
    { id: "temp-id", name: input.name, optimistic: true },
    ...(current ?? [])
  ]);
}

If the mutation fails, the optimistic update is rolled back automatically.

Invalidation and Refetch ​

After a successful mutation, invalidate related resources:

ts
{
  invalidate: [{ type: "products" }],  // Invalidate all "products" tagged resources
  refetch: "active"                     // Refetch only resources currently visible
}
refetch valueBehavior
"active"Refetch only resources with active subscribers
"all"Refetch all invalidated resources
["products.list"]Refetch specific resources by name

Offline Queueing ​

Queue mutations when the browser is offline:

ts
const saveDraft = mesh.mutation("draft.save", {
  offline: true,
  async mutate(input: DraftInput) {
    return api.post<Draft>("/drafts", input);
  }
});

// Persist the offline queue
mesh.persistQueuedMutations({
  key: "app:mutation-queue",
  storage: "localStorage",
  ttl: "1d"
});

// Flush queued mutations when back online
await mesh.runQueuedMutations();

Entity Helpers ​

For list/detail cache sync:

ts
const normalized = mesh.normalizeEntities(products, (p) => p.id);
const merged = mesh.mergeEntities(normalized, [updatedProduct], (p) => p.id);
const list = mesh.denormalizeEntities(merged);

Example: Update Product ​

ts
const updateProduct = mesh.mutation("products.update", {
  optimistic(state, input: { id: string; data: Partial<Product> }, ctx) {
    ctx.setResourceData(productsResource, {}, (current) =>
      (current ?? []).map((p) =>
        p.id === input.id ? { ...p, ...input.data, optimistic: true } : p
      )
    );
  },
  async mutate(input) {
    return api.put<Product>(`/products/${input.id}`, input.data);
  },
  invalidate: [{ type: "products" }],
  refetch: "active"
});

Important Notes ​

TIP

Mutations and resources work together. Mutations write data and invalidate resource tags; resources refetch and update the cache.

WARNING

If optimistic is defined but rollback is not explicitly set, rollback defaults to true. Set rollback: false explicitly if you don't want automatic rollback.

TIP

The offline queue persists across page reloads when used with persistQueuedMutations. Queued mutations flush automatically when the browser reconnects.

Next Steps ​

Released under the MIT License.