Introduction to Offline Web Apps — Part II

Swati Sucharita
8 min readMar 24, 2019

--

This is a fun series about offline web apps. If you have not read the Part I, you may want to read it.

In Part I, we built one basic website which works when the user does not have network connection. We added content to Cache storage using Service Workers, that’s what enabled us to view our pages without network connection.

We will use the code built in Part I as our boilerplate for this article.

In this article we will focus on different caching strategies. At the end of it, we will use different strategies optimal for different scenarios. We will integrate with API to get JSON data. We will make use of IndexedDB for storing our JSON API responses.

Let’s get started

We can use following caching strategies to store our content or data.

  • Cache only strategy: We assume data is always available in the cache, we don’t try to go to network to find it. Optimal for fetching the static contents of a website. Can be images or layout. The contents which do not change frequently.
  • Cache then Network strategy: First we try to show whatever data we have in the cache. We send a request to the network also, if the network sends any data, we refresh the content. Optimal in case slow networks.
  • Network with cache fallback strategy: First we try to connect to the network. If not able to reach to the network, we try to get the content from the cache and show it.
  • Cache with Network fallback strategy: We try to get data from cache, if only content not available in cache we go to network to fetch the data.
  • Network only strategy: We always go to network to fetch data.

We can implement the cache only strategy for the static assets first

Let’s define all the files we think don’t change that often as static content.

We use cache only strategy while requesting these assets. While we request for these files, we get it from cache directly. We assume we don’t need to go to network. These files are available in cache always.

Let’s look at the scenario when we are not able to load a page, it’s not in our cache at all. How do we handle the scenario. For that let’s add a new page to reproduce this scenario.

Let’s add an about folder to public directory and a about/index.html page.

We bump up the cache version in the service worker to get the new content. We close all our tabs and open the application in a new tab (Activate the new service worker).

We can see two pages now Home page and about page.

Let’s try to stimulate the scenario when we are offline and trying to access some page not available in the cache.

Close all tabs, open the application in a new tab. To be sure, we can go to Chrome Dev Tools > Application > Clear Storage and clean all the data for the app and reload http://localhost:8080.

Now let’s go offline and try to visit “About” page.

Of course we can’t show this page, we don’t have this page in the cache. But we can handle this experience gracefully by showing one fallback page. Let’s add that next.

If we go online now and refresh the page, this page gets added to our dynamic cache. So next time we go offline, we still can access the about page. But for the first time visits in offline mode, we need to add a fallback page.

Let’s add an offline.html page to our public folder.

Now we have to add this page to our pre-cached pages so that this page is always available in the cache. Then in scenarios we can’t contact the server and we don’t have the page in our cache, we can use this page as our fall back page.

Let’s go to the sw.js file and make this change.

Now let’s clear our cache to test our scenario. We reload the home page and go offline. If we try to visit the about page (which is not cached yet), we should see the offline page with a link to the home page.

Now we have a much better user experience than the “No internet” page.

We cached static contents mostly this far. Let’s look at how do we cache the JSON API responses. We can store it in the Indexed-db. Indexed-db is a transactional database in our browser. It has pretty good browser support.

Indexed-db support from Can I use

Let’s first build our JSON APIs. I am going to use Firebase here to create the APIs. Firebase is an easy way to setup Node APIs. It uses node server only in the background.

I created an application in Firebase named “tours-offline-demo”. We need a storage area to upload our images and we need a database (Using Real-time database). We will store our data in the database, it will also give us APIs to access those data.

I have uploaded the image we have used in the tutorial to storage. Built a Database record for tours using the information we are using in the database.

Uploaded image to storage
Setup the real-time database

For more information about Firebase setup, please follow the Firebase docs.

Let’s add the API to our application now. Instead of rendering the static card, we will build the cards dynamically from the JSON API response.

Let’s add the following code to the our app.js to fetch tours JSON data and build the cards dynamically.

Our request and response will be cached in the dynamic cache according to our implementation. But there are better ways to store the JSON data, Indexed-DB.

I am going to use idb.js to interact with Indexed-DB. As I am not using any building system like Webpack, I am going to include the file in the application and use it.

For more information about idb.js, please follow the amazing library in Github

I am creating another file called utility.js, that’s a wrapper on top of the idb calls. It is just a set of more specific named functions.

Let’s add the utility.js file in location “src/js/utility.js”

We create a DB called “tours-db” and a object store (table) called “tours”. We have some utility functions to read, write and delete data.

Let’s go to the service worker and add the code to use the indexed-DB to store our data.

Let’s visit the browser now. If we activate the new version of service worker, we can see our data stored in the indexed DB.

We are only storing the data in indexed DB. Let’s make the change to read the data from Indexed DB as well.

And we will also follow our last caching strategy here i.e: “Cache then Network strategy”. We first show whatever data we have in the cache, also we fire a request to the network. When the network data comes back we refresh the data in the UI.

In our src/js/app.js file, I added the code to fetch data from indexed DB and update when we get the response from the network.

Also I added another method to clear the cards from UI before refreshing it.

We also have to add our Utility.js file and the idb.js file to index.html. So that we can access in from the app.js.

We bump up the version number in our cache as always and activate it in the browser.

If we go to the network tab in our browser, we can see two calls getting fired for tours. One fetching data from service worker another from network.

I am going to add some more data in the Firebase so that we can see it working more clearly.

Let’s see if we are able to see the new data in the UI.

Yes, we can. :)

Congratulations!! We have reached to the end of the blog about advanced caching strategies in our PWA series.

You can see all the changes we made in the following repository.

Thank you for reading. Give it a clap if you like it.

--

--