Learn how to use the Cache API to make your application data available offline.
TheCache APIis a system for storing and retrieving network requests and their corresponding responses. These might be regular requests and responses created in the course of running your application, or they could be created solely for the purpose of storing data for later use.
The Cache API was created to enable service workers to cache network requests so that they can provide fast responses, regardless of network speed or availability. However, the API can also be used as a general storage mechanism.
Where is it available?
The Cache API is available inall modern browsers.It is
exposed via the globalcaches
property, so you can test for the presence of
the API with a simple feature detection:
constcacheAvailable='caches'inself;
The Cache API can be accessed from a window, iframe, worker, or service worker.
What can be stored
The caches only store pairs ofRequest
and
Response
objects, representing HTTP requests and responses,
respectively. However, the requests and responses can contain any kind of data
that can be transferred over HTTP.
How much can be stored?
In short,a lot,at least a couple of hundred megabytes, and potentially hundreds of gigabytes or more. Browser implementations vary, but the amount of storage available is usually based on the amount of storage available on the device.
Creating and opening a cache
To open a cache, use thecaches.open(name)
method, passing the name of the
cache as the single parameter. If the named cache does not exist, it is
created. This method returns aPromise
that resolves with theCache
object.
constcache=awaitcaches.open('my-cache');
// do something with cache...
Adding to a cache
There are three ways to add an item to a cache -add
,addAll
,andput
.
All three methods return aPromise
.
cache.add
First, there iscache.add()
.It takes one parameter, either aRequest
or a URL (string
). It makes a request to the network and stores the response
in the cache. If the
fetch fails, or if the status code of the response is not in the 200 range,
then nothing is stored and thePromise
rejects. Note that cross-origin
requests not in CORS mode cannot be stored because they return astatus
of
0
.Such requests can only be stored withput
.
// Retreive data.json from the server and store the response.
cache.add(newRequest('/data.json'));
// Retreive data.json from the server and store the response.
cache.add('/data.json');
cache.addAll
Next, there iscache.addAll()
.It works similarly toadd()
,but takes an
array ofRequest
objects or URLs (string
s). This works similarly to
callingcache.add
for each individual request, except that thePromise
rejects if any single request is not cached.
consturls=['/weather/today.json', '/weather/tomorrow.json'];
cache.addAll(urls);
In each of these cases, a new entry overwrites any matching existing entry. This uses the same matching rules described in the section on retrieving.
cache.put
Finally, there iscache.put()
,which allows you to store either a response
from the network, or create and store your ownResponse
.It takes two
parameters. The first can either be aRequest
object or a URL (string
).
The second must be aResponse
,either from the network, or generated by your
code.
// Retrieve data.json from the server and store the response.
cache.put('/data.json');
// Create a new entry for test.json and store the newly created response.
cache.put('/test.json', new Response('{ "foo": "bar" }'));
// Retrieve data.json from the 3rd party site and store the response.
cache.put('https://example.com/data.json');
Theput()
method is more permissive than eitheradd()
oraddAll()
,and
will allow you to store non-CORS responses, or other responses where the status
code of the response is not in the 200 range. It will overwrite any previous
responses for the same request.
Creating Request objects
Create theRequest
object using a URL for the thing being stored:
constrequest=newRequest('/my-data-store/item-id');
Working with Response objects
TheResponse
object constructor accepts many types of data, including
Blob
s,ArrayBuffer
s,FormData
objects, and strings.
constimageBlob=newBlob([data],{type:'image/jpeg'});
constimageResponse=newResponse(imageBlob);
conststringResponse=newResponse('Helloworld');
You can set the MIME type of aResponse
by setting the appropriate header.
constoptions={
headers:{
'Content-Type':'application/json'
}
}
constjsonResponse=newResponse('{}',options);
If you have retrieved aResponse
and wish to access its body, there are
several helper methods you can use. Each returns aPromise
that resolves
with a value of a different type.
Method | Description |
---|---|
arrayBuffer |
Returns anArrayBuffer containing the body, serialized to
bytes.
|
blob |
Returns aBlob .If theResponse was created
with aBlob then this newBlob has the same
type. Otherwise, theContent-Type of the
Response is used.
|
text |
Interprets the bytes of the body as a UTF-8 encoded string. |
json |
Interprets the bytes of the body as a UTF-8 encoded string, then tries
to parse it as JSON. Returns the resulting object, or throws a
TypeError if the string cannot be parsed as JSON.
|
formData |
Interprets the bytes of the body as an HTML form, encoded either as
multipart/form-data or
application/x-www-form-urlencoded .Returns a
FormData
object, or throws aTypeError if the data cannot be parsed.
|
body |
Returns aReadableStream for the body data. |
For example
constresponse=newResponse('Helloworld');
constbuffer=awaitresponse.arrayBuffer();
console.log(newUint8Array(buffer));
// Uint8Array(11) [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]
Retrieving from a cache
To find an item in a cache, you can use thematch
method.
constresponse=awaitcache.match(request);
console.log(request,response);
Ifrequest
is a string the browser converts it to aRequest
by calling
new Request(request)
.The function returns aPromise
that resolves to
aResponse
if a matching entry is found, orundefined
otherwise.
To determine if twoRequests
match, the browser uses more than just the URL. Two
requests are considered different if they have different query strings,
Vary
headers, or HTTP methods (GET
,POST
,PUT
,etc.).
You can ignore some or all of these things by passing an options object as a second parameter.
constoptions={
ignoreSearch:true,
ignoreMethod:true,
ignoreVary:true
};
constresponse=awaitcache.match(request,options);
// do something with the response
If more than one cached request matches then the one that was created first is
returned. If you want to retrieveallmatching responses, you can use
cache.matchAll()
.
constoptions={
ignoreSearch:true,
ignoreMethod:true,
ignoreVary:true
};
constresponses=awaitcache.matchAll(request,options);
console.log(`There are${responses.length}matching responses.`);
As a shortcut you can search over all caches at once by usingcaches.match()
instead of callingcache.match()
for each cache.
Searching
The Cache API does not provide a way to search for requests or responses
except for matching entries against aResponse
object. However, you can
implement your own search using filtering or by creating an index.
Filtering
One way to implement your own search is to iterate over all entries and
filter down to the ones that you want. Let's say that you want to find all
items that have URLs ending with.png
.
asyncfunctionfindImages(){
// Get a list of all of the caches for this origin
constcacheNames=awaitcaches.keys();
constresult=[];
for(constnameofcacheNames){
// Open the cache
constcache=awaitcaches.open(name);
// Get a list of entries. Each item is a Request object
for(constrequestofawaitcache.keys()){
// If the request URL matches, add the response to the result
if(request.url.endsWith('.png')){
result.push(awaitcache.match(request));
}
}
}
returnresult;
}
This way you can use any property of theRequest
andResponse
objects to
filter the entries. Note that this is slow if you search over large sets of
data.
Creating an index
The other way to implement your own search is to maintain a separate index of entries that can be searched and store the index in IndexedDB. Since this is the kind of operation that IndexedDB was designed for it has much better performance with large numbers of entries.
If you store the URL of theRequest
alongside the searchable properties
then you can easily retrieve the correct cache entry after doing the search.
Deleting an item
To delete an item from a cache:
cache.delete(request);
Where request can be aRequest
or a URL string. This method also takes the
same options object ascache.match
,which allows you to delete multiple
Request
/Response
pairs for the same URL.
cache.delete('/example/file.txt', {ignoreVary: true, ignoreSearch: true});
Deleting a cache
To delete a cache, callcaches.delete(name)
.This function returns a
Promise
that resolves totrue
if the cache existed and was deleted, or
false
otherwise.
Thanks
Thanks to Mat Scales who wrote the original version of this article, which first appeared on WebFundamentals.