I've been playing with the Google Web Starter Kit (https://github.com/google/web-starter-kit) and have got a little progressive web app working but am stuck on one thing: caching static files from external CDNs. E.g. I'm using MDL icons from https://fonts.googleapis.com/icon?family=Material+Icons I can't see a way to cache the request as the service worker only responds to URLs within my app domain.
Options I see: 1. Download the file and put it in a vendor folder. Advantages: easy to set up SW cache. Disadvantages: file won't stay up to date as new icons are added (though that won't really matter as my code will only use the icons available).
Use the NPM repo: https://www.npmjs.com/package/material-design-icons and use build step to copy CSS file from node_modules. Advantages: will allow auto-updating from NPM. Disadvantages: slightly more complex to set up.
Some fancy proxy method that would allow me to use the SW to cache an external URL. e.g. myapp.com/loadExternal?url=https://fonts.googleapis.com/icon?family=Material+Icons
I'm leaning towards 2 right now but would be cool to know if 3 is possible.
TLDR: Try Option 3. You'll thank me later.
From Google Docs:
By default, fetching a resource from a third party URL will fail if it doesn't support CORS. You can add a no-CORS
option to the Request to overcome this, although this will cause an 'opaque' response, which means you won't be able to tell if the response was successful or not.
So
Option 1
Add no-cors
header
var CACHE_NAME = 'my-site-cache-v1';
var urlsToPrefetch = [
'/',
'/styles/main.css',
'/script/main.js',
'https://fonts.googleapis.com/icon?family=Material+Icons'
];
self.addEventListener('install', function(event) {
// Perform install steps
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened cache');
// Magic is here. Look the mode: 'no-cors' part.
cache.addAll(urlsToPrefetch.map(function(urlToPrefetch) {
return new Request(urlToPrefetch, { mode: 'no-cors' });
})).then(function() {
console.log('All resources have been fetched and cached.');
});
})
);
});
As OP said, when the resource is updated, it's hard to get the latest copy in this scenario. And another issue is, as I said you won't know whether the response was a success or not.
Option 2
Or like OP said, we can create a proxy server: Something simple as (Pseudocode, not tested, Node Express code)
var request = require('request');
app.get('/library', function(req,res) {
// read the param
var thirdPartyUrl = req.query.thirdPartyUrl;
request(thirdPartyUrl).pipe(res);
});
And when you goto /library?thirdPartyUrl=https://fonts.googleapis.com/icon?family=Material+Icons
should give you the response and cache it how we normally cache our response. For Ex: remove no-cors
& replace urlsToPrefetch
with below value:
var urlsToPrefetch = [
'/',
'/library?thirdPartyUrl=https://fonts.googleapis.com/icon?family=Material+Icons',
'/library?thirdPartyUrl=https://fonts.googleapis.com/icon?family=Roboto'
];
Option 3
I think this is the best and easier way. Use workbox. We've tried to create PWA with and without workbox and using workbox was simple.
Read about workbox: https://developers.google.com/web/tools/workbox/
Implement a route like this after the initial setup:
workbox.routing.registerRoute(
new RegExp('^https://third-party.example.com/images/'),
new workbox.strategies.CacheFirst({
cacheName: 'image-cache',
plugins: [
new workbox.cacheableResponse.Plugin({
statuses: [0, 200],
})
]
})
);