Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I cache external URLs using service worker?

Tags:

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).

  1. 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.

  2. 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.

like image 561
jeznag Avatar asked Sep 11 '16 03:09

jeznag


People also ask

How do I cache a service worker?

To do that, you can use the cache storage interface within the install event in the service worker. Because the service worker thread can be stopped at any time, you can request the browser to wait for the addAll promise to finish to increase the opportunity of storing all the assets and keeping the app consistent.

How do I create a dynamic cache in a service worker?

We ll put the request into the caches - caches. put with the response clone returned from the fetch promise. Return the response. If promise got rejected fetch will go to caches (pre-cache) to match the request URL request.

How do I cache images with a service worker?

I'd recommend following the approach outlined in this "Service Worker Caching Strategies Based on Request Types" article, and use request. destination inside of your fetch handler to figure out which requests are going to be used for images. self. addEventListener('fetch', (event) => { if (event.


1 Answers

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],       })     ]   }) ); 
like image 188
Asim K T Avatar answered Sep 19 '22 21:09

Asim K T