There are some scenarios when caching the response from an AJAX request will work better than having to make the same request everytime the user refreshes the page.
Such scenarios are easy to identify when you're working with:
- Data that does not update very often.
- APIs with a request limit, such as the GitHub Public API
- Lazy loading elements that do not change very often
For some cases, it would be neccessary to create a complex system to save the response of the request in the AJAX, update it, expire it, keep it up to date, etc. But, for most cases, just a simple way to save it for a period of time is all you need, enter SessionStorage.
SessionStorage is the non so popular brother of LocalStorage, the most common storage brother in the JavaScript Web APIs family. What makes SessionStorage different from LocalStorage? Well, it turns out that whatever you save into SessionStorage expires after the user closes the browser, like every other session.
Given the nature of SessionStorage, a persistent storage that expires just after the user closes the browser, it's the perfect API to save something for a short period of time, whithout having to worry about expiring the data, just save it there and await for it to expire.
Using SessionStorage
SessionStorage has the same API than LocalStorage, the only difference between the tow of them is how they expire the data, while the browser will only delete the data from LocalStorage in case it requires it, SessionStorage data will be delated when the session finishes.
For those who have never used neither LocalStorage or SessionStorage, there's two common operations that you do with them, writing and reading:
//Saves the string Hello world with the key message
sessionStorage.setItem("message", "Hello world");
// Retrieves the string using the key
sessionStorage.getItem("message") // Returns "Hello world"
With these two operations we can now cache the response from an AJAX request, suppose we have to retrieve our repos from GitHub:
function getMyRepos(){
const endpoint = "https://api.github.com/users/urielhdz/repos" //Replace urielhdz with your own username
/* get_data */
}
Now, we'lll using the native fetch operation to retrieve data from the network in order to cache it later:
function getMyRepos(){
const endpoint = "https://api.github.com/users/urielhdz/repos" //Replace urielhdz with your own username
fetch(endpoint).then(r => r.json()); // the json method parses the response into a JSON object
}
To make the code more readable we'll use async functions, but keep in mind that this is not a crucial part of the tutorial, it's only syntactic sugar to make the code more clean:
async function getMyRepos(){
const endpoint = "https://api.github.com/users/urielhdz/repos" //Replace urielhdz with your own username
const data = await fetch(endpoint).then(r => r.json()); // the json method parses the response into a JSON object
}
Now the data constant stores the data response from the GitHub API, and here it comes the interesting part, cache that response to avoid unneccesary requests to the same API.
async function getMyRepos(){
const endpoint = "https://api.github.com/users/urielhdz/repos" //Replace urielhdz with your own username
const key = "githubRepos"; // Key to identify our data in sessionStorage
// First check if there's data in SessionStorage
let data = sessionStorage.getItem(key);
if(data){
// If there's somethin in the sessionStorage with our key, return that data:
return JSON.parse(data);
}
//If there's nothing in the storage, make the AJAX request:
data = await fetch(endpoint).then(r => r.json());
//Then save it into the storage to avoid more requests later on:
sessionStorage.setItem(key, JSON.stringify(data));
return data;
}
Keep in mind that both LocalStorage and SessionStorage can only save strings, so in order to save a more complex structure, such as a JSON object, we have to convert it into a string, that means that when we retrieve the data, we have to parse the string into the previous JSON object.
And that's it, now we have a method that will request data from GitHub only when neccessary, the data will expire as soon as the user closes the browser and that will allow us to keep the data updated.
Drawbacks
As pointed out by Scott Simontis in the comments, this solution with SessionStorage presents some problems:
- SessionStorage is sync, so keep in mind that bot writing and reading data from the storage are operations that will block the main thread of your application.
- Data stored in SessionStorage can be manipulated by the end user, so you shouldn't store sensitive or critical data for your application.
Thanks to Scott for pointing out this problems, as I add in the comments, this solution is aimed to simple and basic problems in which other cache solutions will appear too complex.
Top comments (6)
A lot of developers forget that they have a nearly universal cache available to them...HTTP caching! If you use cache headers correctly, the browser does all of the work for you!
Client-side caching also forces you to consider a lot of security concerns. A user can easily access session state and modify it, compromising the integrity of saved responses and injecting untrusted data that is about to be parsed. SessionStorage is also synchronous, so the retrieval will be a blocking operation.
I would encourage you to explore service workers to handle your caching needs. IndexedDb gives you the power to store more expressive relational data and it is asynchronous to boot!
I believe you can use localforage library to asynchronously store data.
Although I think that your concerns should be highlighted in the article so, what do you think if I add them as a warning, with credits to your comment?
Please feel free to do so! If you have any questions along the way feel free to ask.
This is an alternative for simple cases in which introducing a service worker or an indexedDB database would be an overkill,
It seems limited 5MB of SessionStorage