5 service worker caching strategies for your next PWA app

Share

“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.” — The Offline Cookbook

In this blog post, we will focus on another exciting service workers feature — intercepting and caching network requests.

Caching is one of the most exciting features of SWs. It enables us to deliver to our users a responsive, stable, and native-app-like experience. A web app can display some content and perform some function in bad network conditions, and even when the user is completely offline.

We’ll cover five different caching strategies, and see how and when you should implement each of them:

  • Stale-while-revalidate
  • Cache first, then Network
  • Network first, then Cache
  • Cache only
  • Network only

We’ll address each strategy with an example to help you better understand it. You will also learn when and where to use them in your PWAs, and some assets you can use them with.

Tip: Use Bit (Github) to share, document, and manage reusable React components from different projects. It’s a great way to increase code reuse, speed up development, and build apps that scale.

Example: Exploring shared React components on Bit.dev

Service worker caching strategies

There are strategies we can use with service workers to respond to “fetch” events. These determine how the service worker will respond to a fetch event based on the type of event request.

1. Stale-while-revalidate

This strategy checks for the response in the cache. If it is available, it is delivered and the cache is revalidated. If it is not available, the service worker fetches the response from the network and caches it.

Let’s look at some code:

self.addEventListener('fetch', function (event) {
    event.respondWith(
        caches.open(cacheName)
            .then(function(cache) {
                cache.match(event.request)
                    .then( function(cacheResponse) {
                        fetch(event.request)
                            .then(function(networkResponse) {
                                cache.put(event.request, networkResponse)
                            })
                        return cacheResponse || networkResponse
                    })
            })
    )
});

On a fetch event, the SW checks if the event request is already in the cache by doing cache.match(event.request). It resolves to a Response object cacheResponse; this is the cached response to the event. Then, we perform a new fetch request and put it in the cache. We return the cacheResponse or the networkResponse if the network request is successful.

PWAs use this strategy on high-priority, high-critical files. They also use it on non-GET requests like POST requests. POST requests responses are not cached, because they mutate the datastore every time they are let out, so the responses are different every time.

2. Cache first

This strategy will look for a response in the cache first. If a previously cached response is found, it will return and serve the cache. If not, it will fetch the response from the network, serve it, and cache it for next time.

self.addEventListener('fetch', function (event) {
    event.respondWith(
        caches.open(cacheName)
            .then(function(cache) {
                cache.match(event.request)
                    .then( function(cacheResponse) {
                        if(cacheResponse)
                            return cacheResponse
                        else
                            return fetch(event.request)
                                .then(function(networkResponse) {
                                    cache.put(event.request, networkResponse.clone())
                                    return networkResponse
                                })
                    })
            })
    )
});

The priority in this strategy is to look inside the cache first. If there is something there, it serves it without bothering to get it from the network for revalidation, similar to the stale-while-revalidate method. This strategy only uses the network if no response is found cached; in that case it fetches the response from the network and then caches it.

PWAs use this strategy on files that are non-critical to the web app, assets like images, videos, CSS, etc.

3. Network first

This is the opposite of cache first.

This strategy will try to fetch the response from the network. If it succeeds, it will cache the response and return the response. If the network fails, it will fall back to the cache and serve the response there.

self.addEventListener(’fetch’, function (event) {
    event.respondWith(
        fetch(event.request).catch(function() {
            return caches.match(event.request)
        })
    )
})

PWAs mostly use this strategy for requests that are frequently updating, especially POST requests. Online users get the most up-to-date content, while offline users get the cached version. This is good because the users will have something on their screen served to them while waiting for the network, a better option than having a network error report.

4. Cache only

This strategy responds from the cache only. It does not fall back to the network.

self.addEventListener('fetch', function (event) {
    event.respondWith(
        caches.open(cacheName).then(function(cache) {
            cache.match(event.request).then(function(cacheResponse) {
                return cacheResponse;
            })
        })
    )
})

Without a network to fall back to, it serves the response only from the cache.

This strategy is used on assets that will never change once it is cached the first time. For example, the PWA logo image can never change, so it is cached once and served solely from the cache. There is little to no chance that it will ever change.

5. Network only

This strategy solely uses the network to fetch and serve the response. It does not fallback to any cache.

self.addEventListener('fetch', function (event) {
    event.respondWith(
        fetch(event.request).then(function(networkResponse) {
            return networkResponse
        })
    )
})

Unlike cache only, this strategy is used on assets and files that constantly change.

Conclusion

We have learned some cool caching service worker caching strategies; we also learned how they work with images and examples. Additionally, we learned when and where to use the caching strategy, which is vital.

If you have any questions regarding this or anything I should add, correct, or remove, please comment, email, or DM me.

Thanks!

Copyright ©2024 Educative, Inc. All rights reserved