đź–Ą TECH >> Caching images with Workbox service worker for an Angular app

Gautam Panickar SS
5 min readJul 19, 2020
Photo by ELEVATE from Pexels

I presume that you readers have a basic understanding of service workers, PWA and Googles’ Workbox before you begin reading this article. If not, here’s a good book to learn things .

https://livebook.manning.com/book/progressive-web-apps/part-1/

This is not going to be an in-depth tutorial, but I certainly am thinking about doing one in the near future. This article shall address 2 methods to store images locally. First one in the service worker cache and the second method stores images in IndexedDB.

I am going to write my service worker in Typescript, since my application was written in angular. In an angular app, you will first have to register the service worker. This is preferably done in main.ts. Or if you want to have more control on the services/stores/components depending on the interaction with the service worker, then you can register in the root component app.component.ts

See, the important thing here is the wb.register() method . This is solely responsible for registering your service worker. And also, don’t freak out over the line given below.

const wb = new Workbox('service-worker.js')

This is the .js file after compilation of our SW written in TS. Let’s assume before that our SW will have name service-worker

Now, we will have to create a service worker. Because we are going to create it in Typescript, we need proper configuration to transpile it to JS and then provide new scripts that will in-turn generate the compiled service worker with angular productions build.

I have dedicated an entire folder sw for my service worker and put all related files into it for better separation.

Folder structure

As you can see service-worker.ts file has the main SW code. The handler code is separated in the sw-handlers.ts rest of the files handle the configuration for TS, Webpack and Workbox. Here is the code for the config files.

TS config resolves two files namely, service-worker.ts, sw-handler.ts. Note that swtest is our app name. Angular generates a folder with swtest inside dist folder when built in production. I am not going to go into the angular details as it is out of context.

In short, webpack.sw.config.js tells webpack to find service-worker.ts , load it/all ts files with ts-loader , compile and build in production(handles minification) and write to dist/swtest/service-worker.js file. I hope you now understand why we referred to the .js file while registering the workbox.

worlbox.config.js is a very simple and common workbox config that you can find anywhere in web. An interesting point to note here is that we refer to the same file in swSrsc and swDest .That is because we write over SW in TS and is then compiled to JS in /dist.

But where’s the Service Worker. Show me some code for real! Here it is.

Take note of the comments written in the onMesage event listener. I don’t want to repeat it again :)

I told you that we are going to use 2 strategies to store images locally. One by using cache and the other by leveraging IndexedDB. So, for the IDB strategy we use initIndexedDb() to open DB with name swtest and then create an object store with name images. The logic is handled by sw-handlers.ts, so we instantiate that with the opened DB connection.

bgImagesMatcher matches request URL of format /files/bg_images/

For example: https://gplearns.com/files/bg_images/img_1.png?secret=sdfdsf123&key=dhdhs1234234

We can either use bgImagesDBHandler or bgImagesCacheHandler for the route. But both at the same time would be totally inappropriate. So, I have commented out the option which stores image in IDB for the time being.

bgImagesDBHandler and bgImagesCacheHandler Where are they?

You can find the handlers in sw-handlers.ts. Diving deep into the handlers code, you might think why I wrote a cache handler for myself rather than using the caching strategies and techniques of Workbox. That’s because, the images that I try to fetch from my server has a secret appended to it as a query param to it. This secret changes for each image fetch. In short, the URLs of the images that I fetch from my server are dynamic even though the images are same.

Why can’t I provide bgImagesMatcher as mentioned above and use a workbox strategy like this?

registerRoute(
bgImagesMatcher,
new strategies.CacheFirst()
);

Because the secret changes every time, same image will be cached more than once and in effect I won’t be able to make the use of the actual solution that workbox caching offers. So, that’s why decide to write a cache handler for myself.

Here’s sw-handlers.ts

Storing in cache is the easiest way.

bgImagesCacheHandler makes use of getCacheImageURL method to generate key for caching the image. This key is actually generated by stripping off the changing query parameters. caches gives you direct access to the CacheStorage API. If a match for the key was found, the image is returned.

Otherwise, the intercepted request for fetching images are resumed and then the image response is again intercepted by the handler firstly and then returned to the original request initiating angular component. A clone of the image response is then put into cache bg-images with the key generated using getCacheImageURL.

Putting images into cache or IndexedDB is entirely dependent on the use-case of your application. Check for the advantages of each and compare with your use-case to decide on an approach. Normally, anyone would prefer cache over IDB for storing images.

Now if you want to store images in IndexedDB then have a look at bgImagesDBHandler. Again I need a key here to refer to the images easily. I have used a different method getImageIdFromURL for that.

Okay I don’t know why I used two different methods for generating keys from URL. May be it happened as I was coding. That doesn’t look good anyway. However, I am too lazy to correct that. Please excuse me :)

Again, I have used a Cache-First strategy here. It’s similar to the way we put images into and retrieved from cache as mentioned above. Except that we now have 2 methods fetchImageFromDB and putImageInDB which helps you achieve the fetching and putting respectively. Also note that the images are stored as blob in IndexedDB. So, after getting the response from the server, convert it as blob.

I don’t think there is a need for me to explain what is going inside fetchImageFromDB and putImageInDB. Anyone familiar with IndexedDB will be able to relate to it quickly.

Finally, to make the building process reliable here are the scripts that you may want to add to your package.json . Make sure that you have the necessary workbox packages installed and also http-server. because you know why, :)

So, that’s all folks! I hope you enjoyed reading and found it useful. I might have made mistakes here and there. Please let me know if it can be done more efficiently and be free to point out the mistakes.

Here are the links which I referred to in my pursuit of learning PWAs and service workers.

https://html.spec.whatwg.org/multipage/workers.html
https://www.html5rocks.com/en/tutorials/workers/basics/

https://developers.google.com/web/fundamentals/primers/service-workers/

https://developers.google.com/web/tools/workbox/guides/get-started

https://codelabs.developers.google.com/codelabs/workbox-indexeddb/#0

https://medium.com/runic-software/simple-guide-to-workbox-in-angular-197c25396e68

--

--