State of the Angular Service Worker

by Stephen Fluin

2017-06-01

State of the Angular Service Worker
Service worker support first landed in Angular over a year ago, but things have dramatically improved since those early days.

Setting it up

Taking advantage of service workers is as simple as running two commands in any CLI project:
ng set apps.0.serviceWorker=true
npm install @angular/service-worker
``` 

These commands will enable and install the service worker into your project. Now any time that you run a production build with `ng build -prod`, the service worker will automatically be added to your project.

To get some of the basic features of a PWA, that's all you need to do. The service worker in your production build will install itself, and generate a configuration that helps it automatically cache all of the files generated statically as part of your build process.

## Configuring your Service Worker

Now that your application has static file caching and some of the most basic features. Let's look at the other features you can configure.

All configuration is done in an `ngsw-manifest.json` file in the root of your project. You can create this file with an empty object, and then add features and parameters as you need.

Here's an example of a completed configuration file.
{
"external": {
    "urls": [
        {"url": "https://fonts.googleapis.com/css?family=Montserrat|Raleway|Roboto|Roboto+Mono"},
        {"url": "https://fonts.gstatic.com/s/montserrat/v10/zhcz-_WihjSQC0oHJ9TCYPk_vArhqVIZ0nv9q090hN8.woff2"}
    ]
},
"static.ignore": [
    "\\.js\\.map$"
],
"static.versioned": [
    "\\.[0-9a-z]{20}\\."
],
"dynamic": {
    "group": [
        {
            "name": "firebase",
            "urls": {
                "https://firebasestorage.googleapis.com/v0/b/fluindotio-website-93127.appspot.com": {
                    "match": "prefix"
                }
            },
            "cache": {
                "optimizeFor":"performance",
                "maxAgeMs": 360000000,
                "maxEntries": 40,
                "strategy": "lru"
            }
        },
        {
            "name": "offline",
            "urls": {
                "https://fluindotio-website-93127.firebaseio.com/posts.json": { "match": "prefix"}
            },
            "cache": {
                "optimizeFor": "freshness",
                "maxAgeMs": 360000000,
                "maxEntries": 1,
                "strategy": "lru"
            }
        }
    ]
    
},
"routing": {
    "index": "/index.html",
    "routes": {
        "/": {
            "prefix": false
        },
        "/blog": {
            "prefix": true
        },
        "/bio": {
            "prefix": false
        },
        "/talks": {
            "prefix": false
        }
    }
}
} ```
Let's look at these top level configuration tools one by one starting with the most common. You can infer most of the specific syntax from the above example.

routing

The Service Worker must decide which URLs to serve up your index page for. This section means that when a user attempts to access /my/sub/url, it knows to serve up your index page from your configuration.
This is one of the features that you have to configure manually today, but the ng-pwa-tools thare are being integrated into the CLI will take care of this for you in the future.

externals

The externals section allows you to instruct the Service Worker to cache files from external sites that are not present as part of the list of files emitted by your webpack build. Often this will be used for things like fonts, data files, and other files you do not serve yourself.

static.ignore

By default, the Service Worker caches all of the files emitted from webpack, but you may want to restrict this set. In particular, maybe you don't want to fetch and cache all of your map files, or perhaps some of your content-based image assets.

static.versioned

This configuration setting lets the Service Worker know that if the filename matches the given regular expression, then the file can be versioned by filename alone. This is useful if your webpack build generates hashes within the filename, because the Service Worker does not need to fetch or verify that it is the latest version of the file.

dynamic

The last extremely powerful capability of Service Workers is to inform them about how to cache data that is fetched at runtime. By default, files that are fetched at runtime are not cached, but by creating a dynamic cache group, matching files that are fetched by the Service Worker will also be cached. Choosing optimizeFor setting of performance will always serve from the cache first, and then attempt to cache any updates. Choosing freshness means that the Service Worker will attempt to access the file via the network, and if the network fails, then it will serve the cached copy.

Pulling it all together

Once you have setup your ngsw-manifest.json file with all of the settings you want, this file will automatically be pulled in whenever you run ng-build, and so all of capabilities will automatically start working the next time your users hit your deployed site.
If you are having trouble understanding what's going on with your service worker, check out https://<my-domain>/ngsw.log this is a special URL intercepted by the Service Worker to report its current status.

Doing more

There are lots of other cool things that you should do, but I might recommend waiting until Angular tooling catches up and does these things for you.
  • Manage developer and user expectations by automatically updating service workers, or by prompting the user to restart the page with a toast or popup message
  • Automatically generate SW configured routes
  • Delay Service Worker registration until after your application has bootstrapped, so as to not compete for resources with the critical path of page boot