data-fetching-cachelisted
Install: claude install-skill soumit-kaz/lazysitter
# Data fetching and cache correctness
## Query keys are the contract
A key identifies a cached result. Get it wrong in two directions:
- **too coarse** — `['users']` for a filtered, paginated list means every filter change collides on one entry, and the wrong data flashes on screen;
- **too fine** — a key including a value that changes every render (an object literal, a `Date.now()`) means the cache never hits and every render refetches.
A key must include **every input that changes the result**: the resource, the id, the filters, the pagination, the sort, and the locale if the server localizes. Serialize it stably — an object literal in a key is a fresh identity each render unless the library normalizes it.
Record the repo's existing convention before inventing one; a feature with a different key shape is a feature nothing else can invalidate.
## Invalidation edges are the part that gets forgotten
**Every mutation must name which keys it invalidates.** The "list still shows the deleted row" bug is always a missing edge, and it is invisible in review of any single file because the mutation and the list live apart.
Write them as a table in the plan:
```
mutation invalidates
createItem → ['items', listFilters] ['items','count']
deleteItem → ['items', *] ['item', id] ['items','count']
updateItemName → ['item', id] ['items', *] (name is rendered in the list)
```
That last one is the case people miss: a mutation that "only" changes a detail