Coordinate a query and a mutation
Coordinate one todo read and write through shared server-data state.
Table of contents
APIs used
- mount
- Starts a Valyrian.js application on a DOM target.
- QueryClient
- Creates query and mutation handles over a shared cache with invalidation and persistence controls.
Run this recipe
- Starting project
- Use the local TSX and ESM project from Stage 1 with Valyrian.js installed and its ESM browser build configured.
- File
- Replace src/client-entry.tsx with this recipe snippet.
- Run
- Run npm run build, serve public with npx --yes serve@14.2.5 public --listen 8000, open http://localhost:8000 and use the example's visible controls.
- Observable result
- The mutation invalidates the query, the second fetch reads both items and the view reports separate query and mutation statuses. The cache remains available until Clear query cache is selected.
Starting point
Create a QueryClient over the asynchronous in-memory source included in the snippet.
Steps
Call query(config) for the list, fetch() to read it, mutation(config) for the write and execute(payload) to add one item. Invalidate the query after success and fetch the current list.
tsximport { mount } from "valyrian.js";
import { QueryClient } from "valyrian.js/query";
type Todo = { id: number; title: string };
const records: Todo[] = [{ id: 1, title: "Learn Valyrian.js" }];
let nextId = 2;
const client = new QueryClient({ staleTime: 30_000 });
const todos = client.query({
key: ["todos"],
fetcher: async (): Promise<Todo[]> => records.map((todo) => ({ ...todo })),
});
const addTodo = client.mutation<{ title: string }, Todo>({
execute: async (todo) => {
const created = { id: nextId, title: todo.title };
nextId += 1;
records.push(created);
return { ...created };
},
onSuccess: () => todos.invalidate(),
});
await todos.fetch();
await addTodo.execute({ title: "Learn Valyrian.js" });
await todos.fetch();
let cacheStatus = `cached todos: ${todos.state.data?.length ?? 0}`;
function QueryStatus() {
return (
<main>
<p>
query: {todos.state.status}; mutation: {addTodo.state.status}
</p>
<p>{cacheStatus}</p>
<button
type="button"
onclick={() => {
client.clear();
cacheStatus = "cache cleared";
}}
>
Clear query cache
</button>
</main>
);
}
mount("body", QueryStatus);Result
The mutation invalidates the query, the second fetch reads both items and the view reports separate query and mutation statuses. The cache remains available until Clear query cache is selected.
Limits
The query and mutation handles retain separate error states. Clear query cache calls client.clear() as an explicit user action.