Skip to main content
Version: v8.0.0

Cache Invalidation

When building dynamic applications, keeping the data displayed to the user up-to-date is crucial. Data can become stale for many reasons - another user might have updated it, a background process changed it, or the current user performed an action that invalidates the existing view. Hyper-fetch provides powerful and flexible cache invalidation mechanisms to ensure your app's data is always fresh.


What you'll learn
  1. What cache invalidation is and why it's essential.
  2. How to invalidate the cache using client.cache.invalidate with request instances, strings, and RegExp.
  3. How to batch-invalidate multiple cache entries in a single call.
  4. How to read, update, and delete cache entries directly.
  5. How invalidation triggers automatic re-fetching in React hooks.

The Basics

Cache invalidation marks cached data as stale by setting its staleTime to 0. The next consumer - whether a React useFetch hook or a manual send() call with caching enabled - will see the data is out of date and trigger a fresh request to the server.

Every cached request is identified by a unique cacheKey, which is derived from the request's method, endpoint, and query parameters (e.g. GET_/users or GET_/users?page=2). Invalidation works by targeting these keys.


Invalidating by Request, String, or RegExp

The client.cache.invalidate method accepts three different argument types, giving you flexible control over what gets invalidated:

import { client, getUsers, getUser } from "./api";

// 1. Pass a request instance - uses the request's cacheKey and scope
client.cache.invalidate(getUsers);

// 2. Pass a cacheKey string directly
client.cache.invalidate("GET_/users");

// 3. Pass a RegExp to match multiple cache entries at once
client.cache.invalidate(new RegExp("GET_/users"));
When is it helpful?
  • After a mutation (create, update, delete) to refresh related lists.
  • When you need to force fresh data from the server for a specific resource.
  • When external events (e.g. websocket notifications) indicate stale data.
  • When you need to bulk-invalidate a family of related endpoints (e.g. all paginated pages).

Invalidation After Mutations

The most common pattern is invalidating a list after adding, updating, or deleting an item. Here is a practical example:

const getUsers = client.createRequest()({
endpoint: "/users",
method: "GET",
});

const addUser = client.createRequest<{
response: { id: number; name: string };
payload: { name: string };
}>()({
endpoint: "/users",
method: "POST",
});

// 1. Fetch and cache the users list
const { data } = await getUsers.send();
console.log("Initial users list:", data);

// 2. Add a new user
const { success } = await addUser.send({
payload: { name: "New User" },
});

if (success) {
console.log("New user added successfully.");
// 3. Invalidate the list so hooks will re-fetch automatically
client.cache.invalidate(getUsers);
}

Batch Invalidation

You can invalidate multiple cache entries in a single call by passing an array. The array can mix request instances, strings, and RegExp patterns:

// Invalidate several related endpoints at once
client.cache.invalidate([
getUsers,
getUser.setParams({ userId: 1 }),
new RegExp("GET_/users/\\d+/posts"),
]);

This is useful after a complex mutation that affects multiple resources.


Pattern Matching with RegExp

Sometimes you need to invalidate multiple cache entries that follow a pattern. For instance, you might have cached data for individual items (/users/1, /users/2) and a list of all items (/users). A RegExp is perfect for this.

const getArticles = client.createRequest()({
method: "GET",
endpoint: "/articles",
});

const getArticle = client.createRequest()({
method: "GET",
endpoint: "/articles/:id",
});

When a single article is updated, you can invalidate the entire family of article caches:

// This will invalidate every cache entry whose key matches the pattern:
// 1. The main list: GET_/articles
// 2. Individual items: GET_/articles/1, GET_/articles/2
// 3. Paginated lists: GET_/articles?page=1, GET_/articles?page=2
client.cache.invalidate(new RegExp("GET_/articles"));

Reading, Updating, and Deleting Cache

Beyond invalidation, the cache provides direct access methods for advanced scenarios like optimistic updates or debugging:

Reading Cache

const cachedData = client.cache.get(getUsers.cacheKey);

if (cachedData) {
console.log("Cached response:", cachedData.data);
console.log("Cached at:", cachedData.responseTimestamp);
}

Updating Cache Directly

Use cache.update for optimistic updates - modifying the cached data without a network request. The update is merged with the existing cache entry:

// Optimistically add a new user to the cached list
client.cache.update(getUsers, (previousData) => {
if (!previousData) return previousData;
return {
...previousData,
data: [...previousData.data, { id: 999, name: "Optimistic User" }],
};
});

Deleting Cache Entries

Use cache.delete to completely remove an entry from both sync and async storage. Unlike invalidate, which marks data as stale, delete removes it entirely:

// Remove the cached users list entirely
client.cache.delete(getUsers.cacheKey);

Clearing All Cache

// Remove all cached data
await client.cache.clear();

How Invalidation Triggers Re-fetching

When you call client.cache.invalidate, it emits an invalidation event for each matching cacheKey. React hooks like useFetch listen for these events and automatically dispatch a fresh request when the data they depend on is invalidated. This means you don't need to manually re-fetch after invalidating - the hooks handle it for you.

In plain TypeScript (without React hooks), invalidation only marks data as stale. You would need to call send() again to actually re-fetch the data.


Congratulations!

You've learned how to handle cache invalidation in Hyper-fetch!

  • You can invalidate cache using client.cache.invalidate with request instances, strings, or RegExp.
  • You can batch-invalidate multiple entries by passing an array.
  • You can read, update, and delete cache entries directly for advanced use cases.
  • You understand how invalidation automatically triggers re-fetching in React hooks.