cacheTags middleware. The demo runs a sequence of
+ fetch + mutate operations and shows what to expect in your
+ browser's DevTools Network tab.
+ GET /api/todos/:id fetches from the
+ network and populates the{' '}
+ todo cache entry. The second call
+ within the 5s TTL hits the in-memory cache — no
+ network request is made.
+ GET /api/todos with the{' '}
+ todo-list tag.
+ PATCH /api/todos/:id mutation triggers
+ cache invalidation for both the{' '}
+ todo tag (matching the entity) and the{' '}
+ todo-list tag (the collection). The
+ middleware deletes the matching cache entries.
+ idempotency client middleware —
+ every mutating request automatically receives an{' '}
+ X-Idempotency-Key header so the server can
+ deduplicate replays.
+ idempotency() middleware adds a{' '}
+ X-Idempotency-Key header (UUID v4) to
+ every mutating request. When a request is retried,
+ the same key is reused — so retries are
+ deduplicated server-side.
+ idempotency() middleware stores
+ responses keyed by the header value. Replayed
+ requests return the stored response instead of
+ re-executing the handler.
+
- {'client.todos.create.useMutation({ onSuccess: () => invalidate })'}
+ cache invalidation is implicit via cacheTags middleware
- onMutate → cancel + setQueryData → onError → rollback
+ {'useOptimisticMutation(client.todos.update, { queryKey, optimisticUpdate })'}
);
@@ -517,6 +493,203 @@ function ErrorHandlingDemo() {
);
}
+// ── Demo 9: Cache Tag Invalidation ────────────────────────────────────_
+
+function CacheTagDemo() {
+ const [todoId, setTodoId] = useState(1);
+ const [result, setResult] = useState
+ {'endpoint.cacheTag("todo", p => ({ id: p.params.id }))'}
+
+
+ offlineQueue() middleware — automatic queue and replay
+
+ + Tag-based HTTP caching with automatic invalidation driven by + endpoint annotations +
+
+
+
+
+ Cache tags are declared on the server-side endpoint
+ definition via .clearsCacheTag(). Tags flow
+ through the contract metadata to the client automatically.
+
+ ({
+ page: p.query.page,
+ limit: p.query.limit
+ }))
+ .returns(array(TodoSchema));
+
+const UpdateTodo = todosResource
+ .patch(ById)
+ .body(UpdateTodoBodySchema)
+ .clearsCacheTag('todo-list') // invalidates list
+ .clearsCacheTag('todo', p => ({ // invalidates entity
+ id: p.params.id
+ }))
+ .returns(TodoSchema);
+`)
+ }}
+ />
+
+ @cleverbrush/client/react,{' '}
+ useMutation hooks automatically invalidate
+ TanStack Query cache for the affected group.
+ | Option | +Type | +Default | +
|---|---|---|
+ defaultTtl
+ |
+
+ number
+ |
+
+ 0
+ |
+
+ ttlByTag
+ |
+
+
+ {'{'} [tagName: string]: number {'}'}
+
+ |
+
+ {'{ }'}
+ |
+
+ condition
+ |
+
+ (response) => boolean
+ |
+
+ response.ok
+ |
+
+ Set defaultTtl: 0 for invalidation-only mode:
+ GET responses are not cached, but mutations still invalidate
+ entries created by other endpoints.
+
+ Deduplicate replays of mutating requests via idempotency + keys +
+
+
+
+
+ The server-side idempotency() middleware reads
+ the header, stores the response, and replays it for
+ duplicate keys.
+
+
+
+ X-Idempotency-Key header.
+ | Option | +Type | +Default | +
|---|---|---|
+ headerName
+ |
+
+ string
+ |
+
+ "X-Idempotency-Key"
+ |
+
+ keyGenerator
+ |
+
+ (url, init) => string
+ |
+
+ uuid v4
+ |
+
+ condition
+ |
+
+ (url, init) => boolean
+ |
+
+ mutations only
+ |
+
| Option | +Type | +Default | +
|---|---|---|
+ ttl
+ |
+
+ number
+ |
+
+ 86400000 (24h)
+ |
+
+ headerName
+ |
+
+ string
+ |
+
+ "x-idempotency-key"
+ |
+
+ skip
+ |
+
+ (ctx) => boolean
+ |
+
+ non-mutating requests
+ |
+