Working With The JavaScript Cache API

Download as pdf or txt
Download as pdf or txt
You are on page 1of 7

Working with the JavaScript Cache API

blog.logrocket.com/javascript-cache-api

June 17, 2020

Introduction

The Cache API provides a mechanism for storing network requests and retrieving their
corresponding responses during run-time. It can be used in the absence of an internet
connection (or presence of a flaky one) and this makes it integral to the building of
progressive web applications (fully optimised web applications that work offline like
native applications).

Because it is impossible to predetermine your user-base at development time, it is


important to build web services that can be accessed by a broad spectrum of users who
may not have the best hardware or may have slow internet connections.

Progressive web applications were created to ensure that web services work on all
devices. On mobile devices, they are designed to deliver a user experience that is close to
that of native applications. Under the hood, PWAs use service workers to achieve the
ideal behavior, and they leverage the Cache API for extra control over network
resources.

This Google web fundamentals page describes service workers like this:

1/7
A service worker is a script that your browser runs in the background, separate from a
web page, opening the door to features that don’t need a web page or user interaction.
Today, they already include features like push notifications and background sync. In the
future, service workers might support other things like periodic sync or geofencing. A
core feature of a service worker is the ability to intercept and handle network requests,
including programmatically managing a cache of responses.

We can see that caching can play an important role in the workflow of service workers.
This article shows how the Cache API can be used in a service worker, and as a general
mechanism of resource storage.

All the code in this tutorial can be found in this repository, feel free to fork it or send in
a PR.

Detecting the Cache API


In modern browsers, each origin has a cache storage and we can inspect it by opening
the browser developer tools:

On Chrome: Application > Cache > Cache Storage


On Firefox: Storage > Cache

Pro tip: In Chrome, you can visit chrome://inspect/#service-workers and click on the
“inspect” option (directly under the origin of any already opened tab) to view logging
statements for the actions of the service-worker.js script.

The Cache API is available in all modern browsers:

Edge >= 17
Opera >= 27
Safari >= 11.1
Firefox >= 39
Chrome >= 40
iOS Safari = 11.4 >
UC Browser 11.8 >=
Chrome for Android >= 67

Because older browsers may not support the API, it is good practice to check for its
availability before attempting to reference it. The caches property is available on the
window object and we can check that it is implemented in the browser with this
snippet:

if ('caches' in window){

Usage
2/7
The Cache API is a great choice for caching URL-addressable resources, that is, you
should use the Cache API when you work with network resources that are necessary to
load your application. If your application deals with lots of data, you may cache the data
that the user will most likely need on page load. These resources may include file-based
content, assets, API responses, and web pages.

For the storage of significant amounts of structured data (including files/blobs), you
should ideally use the IndexedDB API.

The Cache API ships with several methods to perform the following (CRUD) operations:

1. Create a new cache


2. Add (update) items to a cache
3. Retrieve items from a cache
4. Delete items from a cache

Let’s go over some ways to use these methods in our code.

Create a new cache


Before we can start storing request-response pairs into our cache storage, we need to
create a cache instance. Each origin can have multiple cache objects within its cache
storage. We can create a new cache object using the caches.open() method:

const newCache = await caches.open('new-cache');

The snippet above receives the name of the cache as the single parameter and goes on to
create the cache with that name. The caches.open() method first checks if a cache with
that name already exists. If it doesn’t, it creates it and returns a Promise that resolves
with the Cache object.

After the snippet executes, we will now have a new cache object that can be referenced
with the name new-cache.

Adding items to a cache


There are three main ways to add items to the cache:

1. add
2. addAll
3. put

All of these methods return a Promise , now let’s go over each of these and see how
they differ from one another.

Cache.add()
3/7
The first method, cache.add() , takes a single parameter that can either be a URL string
literal or a Request object. A call to the cache.add() method will make a Fetch request
to the network and store the response in the associated cache object:

newCache.add('/cats.json')

or to gain more control, we can use a request object:

const options = {
method: "GET",
headers: new Headers({
'Content-Type': 'text/html'
}),
}
newCache.add(new Request('/cats.json', options))

Note: If the fetch is unsuccessful and an error response is returned, nothing is stored in
the cache and the
Promise rejects.

Cache.addAll()
This method works similarly to the cache.add() method except that it takes in an array
of request URL string literals or Request objects and returns a promise when all the
resources have been cached:

const urls = ['pets/cats.json', 'pets/dogs.json'];


newCache.addAll(urls);

Note: The promise rejects if one or more items in the array of requests are not cached.
Also, while the items in the array are being cached, a new entry overwrites any matching
existing entry.

Cache.put()
The Cache.put method works quite differently from the rest as it allows an extra layer
of control. The put() method takes two parameters, the first can either be a URL string
literal or a Request object, the second is a Response either from the network or
generated within your code:

// Retrieve cats.json and cache the response


newCache.put('./cats.json')

// Create a new entry for cats.json and store the generated response
newCache.put('/cats.json', new Response('{"james": "kitten", "daniel": "kitten"}'))

// Fetch a response from an external address and create a new entry for cats.json
newCache.put('https://pets/cats.json');

4/7
The put method allows an extra layer of control as it lets you store responses that do
not depend on CORS or other responses that are dependent on a server response status
code.

Pro tip: The first two methods — add() and addAll() — are dependent on the state of
CORS on the server the data is being requested from. If a CORS check fails, nothing gets
cached and the Promise rejects. Using put() , on the other hand, gives you extra
confidence as you can set an in-house response.

Retrieving items from a cache


After we’ve added some items to the cache, we need to be able to retrieve them during
run-time. We can use the match() method to retrieve our cached responses:

const request = '/cats.json';


const response = await newCache.match(request);

In the code above, we passed in a request variable to the match method, if the
request variable is a URL string, it is converted to a Request object and used as an
argument. The match method will return a Promise that resolves to a Response
object if a matching entry is found.

The browser uses different factors in determining if two or more Requests match. A
Request may have the same URL as another but use a different HTTP method. Two
such requests are considered to be different by the browser.

When using the match method, we can also pass an options object as the second
parameter. This object has key value pairs that tell match to ignore specific factors
when matching a request:

// create an options object


const options = {
ignoreVary: true, // ignore differences in Headers
ignoreMethod: true, // ignore differences in HTTP methods
ignoreSearch: true // ignore differences in query strings
}

// then we pass it in here


const response = await newCache.match(request, options);

In a case where more than one cache item matches, the oldest one is returned. If we
intend to retrieve all matching responses, we can use the matchAll() method.

Removing items from a cache


We might not need a cache entry anymore and want it deleted. We can delete a cache
entry using the delete() method:

5/7
const request = '/cats.json';
newCache.delete(request);

In the code above, we saved a URL string in the request variable but we can also pass in
a Request object to the delete method. In a case where we have more than one
matching entries, we can pass in a similar options Object as we did with the match
method.

Deleting a cache
Finally, we can delete a cache by calling the delete() method on the caches property of
the window object. Let’s delete our cache in the snippet below:

// delete an existing cache


caches.delete('new-cache');

Note: When a cache is deleted, the delete() method returns a Promise if the cache
was actually deleted and a false if something went wrong or the cache doesn’t exist.

Conclusion
In this article, we took a tour of the Cache API and discussed its usefulness to the
development of progressive web applications. We also explored its CRUD methods and
saw how easily we can retrieve responses and store requests.

Note: For security reasons, a cache is bound to the current origin and other origins cannot
access the caches set up for other origins.

All the code in this tutorial can be found in this repository, feel free to fork it or send in
a PR.

LogRocket: Debug JavaScript errors easier by understanding


the context
Debugging code is always a tedious task. But the more you understand your errors the
easier it is to fix them.

LogRocket allows you to understand these errors in new and unique ways. Our frontend
monitoring solution tracks user engagement with your JavaScript frontends to give you
the ability to find out exactly what the user did that led to an error.

6/7
LogRocket records console logs, page load times, stacktraces, slow network
requests/responses with headers + bodies, browser metadata, and custom logs.
Understanding the impact of your JavaScript code will never be easier!

Try it for free.

7/7

You might also like