4.2 KiB
Invalidating Cache
A common problem when using a cache-first approach in your client is that there will be times when your server has newer data, your user knows that it has, but their client doesn't.
Here's a simple step to step example of it.
- User list all available posts, server return an empty array.
- User proceeds to create a new post, server returns 200 OK.
- Your frontend navigates to the post list page.
- The post list page still shows 0 posts because it has a fresh cache for that request.
- User gets angry that the post list page is empty, they click the refresh button.
- The in-memory cache is vanished and the post list page now shows the created post.
How to fix it
You should be familiar with the Request ID concept.
There is a cache.update option
available for every request that you make. You can resolve this problem in two "major"
ways:
In these examples, we'll wrap the list-posts and create-post calls inside known
functions, thus, centralizing the api logic into a single function.
Updating the local cache
When you are calling a create-post endpoint, it's almost sure that, if the request
succeed, your list-posts cache will get out of date.
You can update your list-posts cache by using the cache.update option in yours
create-post requests.
By default, you only need to know the id of the list-posts request. There's many ways to
get that information, either by saving the response.id in a object or manually defining
the config.id in each request.
This method only works when you are manually making a known request out of date by calling another endpoint of your API. Similarly with a
list-postsand acreate-postrequest.
function listPosts() {
return axios.get('/posts', {
id: 'list-posts'
});
}
function createPost(data) {
return axios.post('/posts', data, {
cache: {
update: {
// Will perform a cache update for the `list-posts` internal cache
'list-posts': (cachedState, createPostResponse) => {
// If the cache is not cached, we don't need to update it
if (cachedState.state !== 'cached') {
return 'ignore';
}
// Imagine the server response for the `list-posts` request is: { posts: Post[]; }
// And the `create-post` response comes with the newly created post.
// Add the current created post to the end of the post's list
cachedState.data.posts.push(createPostResponse.data);
// Return the same cache state, but a updated one.
return cachedState;
}
}
}
});
}
If you want to update the cache for a specific request outside of the request config, you can use the storage api.
if (someLogicThatShowsIfTheCacheShouldBeUpdated) {
const cache = await axios.storage.get('list-posts');
// Edits the current cache
cache.data = '...';
await axios.storage.set('list-posts', cache);
}
Then, every time you create a post, your listPosts function will - without contacting the server - return the updated list of posts.
Invalidating the local cache
This method works by completely erasing the local cache and forcing the client, (whenever needed) request again to your endpoint.
There's about 3 situations that makes this method better than the previous one:
- When you cannot predict the entire data needed.
- When you know that there is newer data available made by another user or service.
- When the data is too bigger or complex to you handle it in your client without the need of duplicating hundreds of service classes.
function listPosts() {
return axios.get('/posts', {
id: 'list-posts
});
}
function createPost(data) {
return axios.post('/posts', data, {
cache: {
update: {
// Will erase the list-posts cache and force the client (when needed) request to the server again
'list-posts': 'delete'
}
}
});
}
If you want to invalidate the cache for a specific request outside of the request config, you can use the storage api.
if (someLogicThatShowsIfTheCacheShouldBeInvalidated) {
// Deletes the current cache
await axios.storage.remove('list-posts');
}