Cache-Tag Middleware
Tag-based HTTP caching with automatic invalidation driven by endpoint annotations
Basic Usage
import { cacheTags } from '@cleverbrush/client/cache';
const client = createClient(api, {
middlewares: [cacheTags({ defaultTtl: 5000 })],
});
// First call hits the network
await client.todos.list({ query: { page: 1 } });
// Within 5 seconds — cache hit, no network request
await client.todos.list({ query: { page: 1 } });
Server Integration
Cache tags are declared on the server-side endpoint definition via .clearsCacheTag(). Tags flow through the contract metadata to the client automatically.
// contract.ts
const ListTodos = todosResource
.get()
.query(TodoListQuerySchema)
.clearsCacheTag('todo-list', p => ({
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);
How It Works
- On GET:Computes cache key from the endpoint's cache tag names and property selectors. Serves cached response if within TTL.
- On mutation (POST/PUT/PATCH/DELETE): Invalidates all entries whose key starts with any of the endpoint's tag names — no manual callbacks needed.
- Property-based keys: Tags with properties differentiate cache entries by request params, query, body, or headers (e.g. different pages get different cache keys).
- TanStack Query bridge: When used with
@cleverbrush/client/react,useMutationhooks automatically invalidate TanStack Query cache for the affected group.
Options
| 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.