Bryan Soltis

@bryan_soltis

Creating a Progressive Web App with a Headless CMS — Part 3

Part 1 | Part 2 | Part 3

Welcome to Part 3! If you’ve haven’t been following this series, I’ve been creating a Progressive Web App (PWA) using a Headless CMS. And the excitement has been unbearable! There’s been SUSPENSE! DRAMA! ACTION! ROMANCE? Yeah, we’ve seen a lot in Part 1 and Part 2, so it’s time to keep the franchise going with the next exciting installment. Grab the Milk Duds.

In the first 2 articles, I covered the basics of creating a PWA and how to integrate a Headless CMS into the mix. I gave an overview of the PWA architecture, detailed some important components, and showed you how to create a working example. Because PWAs are all about providing a dynamic experience, pulling and displaying content is a pretty big deal. How and when you pull that content is equally important, so I wanted to break down how to cache your content within your app. See? I told you this was going to get even more exhilarating.

Why caching?

If you’ve ever built an application that uses a 3rd party service, you know that it doesn’t happen using magic. There are always API calls back and forth as the systems communicate. This may mean pulling data, validating information, or recording the flavor of latte you just ordered. The point is, that communication can quickly add up as users interact with your app. Learning how to make those calls wisely will help your app run better, and keep corporations from knowing your every move.

Caching is used all over the place to reduce calls and improve speed. It comes at a cost, however, in that the data you cache must still be valid when you display it. For a lucky few of you, that means you potentially could cache every aspect of your app and get amazing load times. For the rest of us, it’s going to take some planning to determine what makes the most sense. And because there are so many ways to do caching, the selection process can be a daunting one, especially when you find that you will use different techniques depending on the data.

What is the best solution for your app is entirely up to you. Just keep in mind how big a role caching can (should) play within your PWA to help it run as smooth as possible. It’s all about giving the user a great experience and providing the most relevant content.

Caching solutions

When it comes to caching, you can leverage a number of existing libraries, or roll your own. If you really like working with lower-level components (or are into Crossfit), knock yourself out and make a sweet new solution (while flipping a tire or something) for your app. If you’re like me, then you may be more inclined to leverage an existing library to do the heavy lifting. Just do your homework and find the solution that fits your needs the best.

The Google Offline Cookbook is a great resource for learning about caching in PWAs. I strongly recommend you check it out to see process workflows, scenarios, and deep dives into how data can move through your application.

Read more about the Google Offline Cookbook

For our Pack and Go demo, we opted for Workbox. Workbox is a JS library that provides offline support for PWAs and other applications. Through precaching, Workbox allows you to load aspects of your code quickly from memory, while defining what areas are to be retrieved from external sources and when. What’s even better, it does so with a minimal amount of code and configuration, making implementation a breeze. So, win/win, right?

Read more about Workbox

Caching strategies

NetworkFirst process (from the Google Offline Cookbook)

Those multiple caching methods? Yeah, there’s about a billion of them, depending on your platform, libraries, components, and favorite color. Regardless of type you choose, the main thing to keep in mind with PWAs is the “type” of caching. Here’s a brief overview of the most common options:

CacheFirst
Fetch from the cache. If not available, fetch from the network and update the cache.

CacheOnly
Fetch only from the cache and never from the network.

NetworkFirst
Fetch from the network. If that fails, then fetch from the cache.

NetworkOnly
Fetch only from the network and never from the cache.

StaleWhileRevalidate
Fetch from both. Return from cache if available. If not, return from network and update cache, if successful.

Read more about Caching Methods

You’ll use some (if not all) at various points of your development, depending on how you structure your data and application. Be sure to read up on each option to understand exactly how the requests will be handled so that your app performs how you want it to.

In our demo, we had two distinct types of content.

1. The AppShell (images, styles, headers, etc.)

2. The points of interest content from Kentico Cloud

Because our “main” files would rarely change, we opted for the CacheFirst option. This would load our AppShell and supporting files from cache (after the first load) for a quick display of the interface. Seeing how these files would rarely be modified, this made the most sense and gave us great load times.

For our POI content, we would be loading these using the Kentico Cloud API. Because this information would change often, it made sense that this content would need to be more dynamic. To accommodate this requirement, we went with the NetworkFirst strategy. This option allowed the app to always load the most up to date information from our Kentico Cloud project. If the network was down for any reason, we would fall back to the cached version.

Service worker

Whatever caching option you choose, your journey is certainly going to start with your service worker. Being the Heimdal of communication, your service worker will be where you register your caching and determine when it will be invoked. Because your service worker is the proxy between the app and the interwebs, it can intercept every request. It will have access to the cache repository you implement, and make all the components work together. And keep the Frost Giants off the Rainbow Bridge and in their own neighborhood.

Caching Pack and Go

Now that you have an understanding of caching with PWAs, let me tell you how we implemented it in our Pack and Go sample app. If you have been following the series, you may remember that we had a lot of code in our service-worker.js file for defining what files to cache immediately, and when to cache the calls to the Kentico Cloud API. Workbox takes care of all that code by wrapping things up in a pre-built library. This makes configuration super simple and clean, while providing the same functionality.

First, we added the workbox plugin to our package.json file.

“devDependencies”: {

“workbox-webpack-plugin”: “³.0.1”
},

Next, we registered Workbox within our Node app. In our webpack.config.js file, we added a new const and reference to the workbox plugin. This includes code to cache files that match a set of extensions. This replaces the AppShell files definition that used to be in the service worker file in the previous version of the app. Workbox also handles versioning of the AppShell files.

const workboxPlugin = require(‘workbox-webpack-plugin’);
new workboxPlugin.InjectManifest({
swSrc: ‘./src/public/service-worker.js’,
globDirectory: ‘./src/public/’,
globPatterns: [‘**/*.{html,js,css,svg,png,ico,woff,woff2,ttf}’],
});

In our serviceworker.js file, we defined how we would cache calls to the Kentico Cloud API. We registered the specific route to the API, and selected the networkFirst option. This code replaces the addEventListener code from the previous version of the app. This is a lot simpler, as the registration of the listener is handled by Workbox, by default.

if (workbox) {
console.log(`Yay! Workbox is loaded 🎉`);
workbox.routing.registerRoute(
/https:\/\/deliver\.kenticocloud\.com\/66ab95de-6599–0018-f141–3c9dc08fe797\/items/,
workbox.strategies.networkFirst()
);
workbox.precaching.precacheAndRoute(self.__precacheManifest || []);
} else {
console.log(`Boo! Workbox didn’t load 😬`);
}

Testing

With Workbox registered and configured, you should be ready to test your PWA. For our demo, we launched the site locally and confirmed the site loaded correctly.

Viewing the Chrome Developer Tools, we could see that Workbox was being registered and properly caching the responses.

We then deployed the site to an Azure App Service and tested it there.

If you want to see the live demo, check out my site.

If you want to see the full code, check out the Kentico Cloud Pack and Go GitHub project. Be sure to use the v2-caching branch for what we covered today. We are continuing to update the project with new features, which we will cover in future articles.

View the Kentico Cloud Pack and Go GitHub project

Wrapping up

Building an app that performs great in every situation is a big challenge. You must consider the user’s environment, connection, preferences, and a host of other factors. By leveraging caching in your PWAs, you can ensure your content is loaded quickly and correctly. This will increase user satisfaction, and reduce calls between your systems.

I also showed you a lot of great capabilities of Kentico Cloud. By using this Headless CMS, you can centralize your content, and leverage a technology-agnostic API to deliver it wherever you need. This helps you build applications the way YOU want to build them, in any language, while still getting a great content management experience for your editors.

Learn more about Kentico Cloud

This article wraps up my series on Creating a Progressive Web App with a Headless CMS. I hope you learned a lot about this new and exciting development technique. With it, you can build an amazing user experience for your audience, regardless of their platform.

For our Pack and Go PWA, we have big plans coming! Look for future blogs on transitioning to Angular, adding searching, and other leveraging device capabilities with your apps. See you next time!

Part 1 | Part 2 | Part 3

More by Bryan Soltis

Topics of interest

More Related Stories