Reading About the Service Worker API

I want to implement notifications for this single page application, so I want to know how the Service Worker API works.

Date Created:
Last Edited:

References



Definitions


  • Worker
    • The Worker interface of the Web Workers API represents a background task that can be created via script, which can send messages back to its creator.
  • Man in the Middle Attacks
    • "A manipulator in the middle (MitM) intercepts communication between two systems."
    • MTM attacks are tough to defend against. A few tips:
      • Don't just ignore certificate warnings. You could be connection to a phishing server or an imposter server.
      • Sensitive sites without HTTPS encryption on public Wi-Fi networks aren't trustworthy.
      • Check for HTTPS in your address bar and ensure encryption is in-place before logging in.
  • Associated Document
    • The Window object has an associated Document, which is a Document object. It is set when the Window object is created, and only ever changed during navigation from the initial about:blank Document
  • Capability URL
    • Capability URLs grant access to a resource to anyone who has the URL. There are times when this is useful, for example one-shot password reset URLs, but overuse can be problematic as URLs cannot generally be kept secret.


Notes


Service workers essentially act as proxy servers that sit between web applications, the browser, and the network (when available). They are intended, among other things, to enable the creation of of effective offline experiences, intercept network requests, and take appropriate action based on whether the network is available, and update assets residing on the server. They will also allow access to push notifications and background sync APIs.


Concepts and Usage

  • A service worker is an event driven worker registered again an origin and a path. It takes the form of a JavaScript file that can control the web-page/site that it is associated with, intercepting and modifying navigation and resource requests, and caching resources in a very granular fashion to give you complete control over how your app behaves in certain situations (the most obvious being when the network is not available).
  • A service worker is run in a worker context: it therefore has no DOM access, and runs on a different thread to the main JavaScript thread that powers your app, so it is non blocking.
  • It is designed to be fully async; as a consequence, APIs such as synchronous XHR and Web Storage can't be used inside a service worker.
  • Service workers can't import JavaScript module dynamically, and import() will throw if it is called in a service worker global scope. Status import using the import statement is allowed.
  • Service workers only run over HTTPS, for security reasons. Most significantly, HTTP connections are susceptible to malicious code injection by man in the middle attacks, and such attacks could be worse if allowed access to these powerful APIs.

Registration

  • A service worker is first registered using the ServiceWorkerContainer.register() method. If successful, your service worker will be downloaded to the client and attempt installation/activation (see below) for URLs accessed by the user inside the whole origin, or inside a subset specified by you.


Download, Install, and Activate

  • The Service Worker Lifecycle:
  1. Download
    1. The service worker is immediately downloaded when a suer first accesses a service worker-controlled site / page.
    2. It is then updated when:
      1. A navigation to an in-scope page occurs.
      2. An event is fired on the service worker and it hasn't been downloaded in the last 24 hours.
  2. Install
    1. Installation is attempted when the downloaded file is found to be new - either different to an existing service worker (byte-wise compared), or the first service worker encountered for this page / site.
    2. If this is the first time the service worker has been made available, installation is attempted and after successful installation, it is activated.
    3. If there is an existing service worker available, the new version is installed in the background, but not yet activated - at this point it is called the worker in waiting. It is only activated when there are no longer any pages loaded that are still using the old service worker. As soon as there are no more pages to be loaded, the new service worker activates (becomes the active worker). Activation can happen sooner using ServiceWorkerGlobalScope.skipWaiting() and existing pages can be claimed by the active worker using Clients.claim().
    4. You can listen for the install event.
  3. Activate
    1. You can listen for the activate event. The point where this event fires is generally a good time to clean up old caches and other things associated with the previous version of your service worker.
  • Service workers can respond to requests using the FetchEvent event. You can modify the response to these requests in any way you want, using the FetchEvnet.respondWith() method.


Other Use Case Ideas

  • Background data synchronization
  • Responding to resource requests from other origins
  • Receiving centralized updates to expensive-to-calculate data such as geolocation or gyroscope, so multiple pages can make use of one set of data
  • Client-side compiling and dependency management of CoffeeScript, less, CJS/AMD modules for development purposes
  • Custom templating based on certain URL patterns
  • Performance enhancements
  • API mocking
Service Workers bring Progress Web Apps Closed to Native App Viability


Interfaces


  • Cache
    • Represents the storage for Request/Response object pairs that are cached as part of the ServiceWorker life cycle
  • CacheStorage
    • Represents the storage for Cache objects. It provides a master directory of all the named caches that a ServiceWorker can access, and maintains a mapping of string names to corresponding Cache objects
  • Client
    • Represents the scope of a service worker client. A service worker client is either a document in a browser context or a SharedWroker, which is controlled by an active worker.
  • Clients
    • Represents a container for a list of Client objects; the main way to access the active service worker clients at the current origin
  • ExtendableEvent
    • Extends the lifetime of the install and activate events dispatched on the ServiceWorkerGlobalScope, as part of the service worker lifecycle. This ensures that any functional events, like FetchEvent, are not dispatched to the ServiceWorker, until it upgrades database schemas, and deleted outdated cache entries, etc.
  • ExtendableMessageEvent
    • The event object of a message event fired on a service worker (when a channel message is received on the ServiceWorkerGlobalScope from another context) - extends the lifetime of such events
  • FetchEvent
    • The parameter passed into the onfetch handler, FetchEvent represents a fetch action that is dispatched on the ServiceWorkerGlobalScope of a ServiceWorker. It contains information about the request and resulting response, and provides the FetchEvent.respondWith() metho, which allows us to provide an arbitrary response back to the controlled page.
  • InstallEvent
    • The parameter passed into the oninstall handler, the InstallEvent interface represents an install action that is dispatched on the ServiceWrkerGlobalScope of a ServiceWorker. As a child of ExtendableEvent, it ensures that functional events such as FetchEvent are not dispatched during installation.
  • NavigationPreloadManager
    • Provides methods for managing the preloading of resources with a service worker.
  • ServiceWorker
    • Represents a service worker. Multiple browsing contexts (e.g. pages, workers, etc.) can be associated with the same ServiceWorker object.
  • ServiceWorkerContainer
    • Provides an object representing the service worker as an overall unit in the network ecosystem, including facilities to register, unregister, and update service workers, and access the state of service workers and their registrations.
  • ServiceWorkerGlobalScope
    • Represents the global execution context of a service worker
  • ServiceWorkerRegistration
    • Represents a service worker registration
  • WindowClient
    • Represents the scope of a service worker client that is a document in a browser context, controlled by an active worker. This is a special type of Client object, with some additional methods and properties available.

Extensions to Other Interfaces

  • Window.caches
    • Returns the CacheStorage object associated with the current context
  • WorkerGlobalScope.caches
    • Returns the CacheStorage object associated with the current context
  • Navigator.serviceWorker
    • Returns a ServiceWorkerContainer object, which provides access to registration, removal, upgrade, and communication with the ServiceWorker objects for the associated document
  • WorkerNavigator.serviceWorker
    • Returns a ServiceWorkerContainer object, which provides access to registration, removal, upgrade, and communication with the ServiceWorker objects for the associated document


The Service Worker Lifecycle


The lifecycle of the service worker is the most complicated part. If you don't know what it's trying to do and what the benefits are, it can feel like it's fighting you. But once you know how it works, you can deliver seamless, unobtrusive updates to users, mixing the best of web and native patterns.
  • The intent of the lifecycle is to:
    • Make offline-first possible
    • Allow a new service worker to get itself ready without disrupting the current one
    • Ensure an in-scope page is controlled by the same service worker (or no service worker) throughput
    • Ensure there's only one version of your site running at once


The First Service Worker

  • The install event is the first event a service worker gets, and it only happens once
  • A promise passed to installEvent.waitUnit() signals the duration and success or failure of your install
  • A service worker won't receive events like fetch and push until it successfully finished installing and becomes active
  • A page's fetches won't go through a service worker unless the page request itself went through a service worker. So you'll need to refresh the page to see the effects of the service worker
  • clients.claim() can override this default, and take control of non-controlled pages


Scope and Control

  • The default scope of a service worker registration is ./ relative to the script URL.
  • We call pages, workers, and shared worker clients. Your service worker can only control clients that are in-scope. Once a client is controlled, its fetches go through the in-scope service worker. You can detect if a client is controlled via navigator.serviceWorker.controller which will be null or a service worker instance


Download, parse and Execute

  • Your very first service worker downloads when you call .register(). If your script fails to download, parse, or throws an error in its initial execution, the register promise rejects, and the service worker is discarded.


Install

  • The first event a service worker gets is install. It's triggered as soon as the worker executes, and it's only called once per service worker. If you alter your service worker script the browser considers it a different service worker, and it'll get its own install event.
  • The install event is your chance to cache everything you need before being able to control clients. The promise you pass to event.waitUntil() lets the browser know when your install completes, and f it was successful. If it rejects, the browser throws the service worker away.


Activate

  • Once your service worker is ready to control clients and handle functional events like push and sync, you'll get an activate event. But that doesn't mean the page that called .register() will be controlled.


clients.claim

  • You can take control of uncontrolled clients by calling clients.claim() within your service worker once it's activated.


Updating the Service Worker

  • An update is triggered if any of the following happens:


Install

  • Changing the cache name sets up a new cache without overwriting things in the current one. This pattern creates version-specific caches.


Waiting

  • After being installed, the updated service worker delays activating until the existing service worker is no longer controlling clients. This state is called waiting, and it's how the browser ensures that only one version of your service is running at a time.


Activate

  • This fires once the old service worker is gone, and your new service worker is able to control clients. This is the ideal time to do stuff that you couldn't do while the old worker was still in use, such as migrating databases and clearing caches.


Skip the Waiting Process

  • The waiting phase means that you're only running one version of your site at once, but if you don't need that feature, you can make your new service worker activate sooner by calling self.skipWaiting(). This causes your service worker to kick out the current active worker and activate itself as soon as it enters the waiting phase.


Manual Updates

  • You can trigger manual updates with the .update() method. If you expect the user to be using the site for a long time without reloading, you may want to call update() on an interval (such as hourly).


Avoid Changing the URL of your Service Worker Script

  • It is best to update the script at its current location.


Making Development Easy

Update on Reload

  • In the Service Worker tab in Chrome DevTools, check Update on Reload so that you get updates on each navigation.


Skip Waiting

  • You can click skip waiting in Chrome DevTools to promote the service worker to active immediately.


Handling Updates

  • The whole update cycle is observable - there are events that you can listen to and properties on the Service Worker that you can check to see the state of the Service Worker.


Using Service Workers


  • Service workers fix issues related to asset caching and custom network requests, and they seek to provide a better user experience when the user loses connection. Using a service worker you can set an app up to use cached assets first, thus providing a default experience even when offline, before getting more data from the network (commonly known as offline first). This is already available in native apps, which is one of the main reasons why native apps are often chosen over web apps.
  • A service worker functions like a proxy server, allowing you to modify requests and responses replacing them with items from its own cache.

Basic Architecture

  1. The service worker code is fetched and then registered using serviceWorkerContainer.register(). If successful, the service worker is executed in a ServiceWorkerGlobalScope; this is basically a special kind of worker context, running off the main script execution thread; with no DOM access. The service worker is now ready to process events.
  2. Installation takes place. An install event is always the first one sent to a service worker (this can be used to start the process of populating an IndexedDB, and caching site assets). During this step, the application is preparing to make everything available for use offline.
  3. When the install handler completes, the service worker is considered installed. At this point, a previous version of the service worker may be active and controlling open pages. Because we don't want two different versions of the same service worker running at the same time, the new version is not active.
  4. Once all pages controlled by the old version of the service worker have closed, it's safe to retire the old version, and the newly installed service worker receives an activate event. The primary use of activate is to clean up resources used in previous versions of the service worker. The new service worker can call skipWaiting() to ask to be activated immediately without waiting for open pages to be closed.
  5. After activation, the service worker will now control pages, but only those that were opened after the register is successful. In other words, documents will have to be reloaded to actually be controlled, because a document starts life with or without service worker and maintains that for its lifetime. To override this default behavior and adopt open pages, a service worker can call clients.claim()
  6. Whenever a new version of a service worker is fetched, this cycle happens again and remains of the previous version are cleaned during the new version's activation.
  • Summary of service worker events:
    • install
    • activate
    • message
      • The message event of the ServiceWorkerGlobalScope interface occurs when incoming messages are received. Controlled pages can use the ServiceWorker.postMessage() method to send messages back via the service workers. The service worker can optionally send a response back via the Client.postMessage(), corresponding to the controlled page.
    • Functional Events:
      • fetch
      • sync
      • push

The article goes over an example of using the service worker which would be a good idea to reference.


APIs Related to Service Workers


Background Fetch API


  • Background Fetch API Mozilla Docs
  • Not available in Safari, so I'm not going to go into depth on it
  • When an application requires the user to download large files, this often presents a problem when the user needs to stay connected to the page for the download to complete. If they lose connectivity, close the tab or navigate away from the page the download stops.
  • The Background Synchronization API provides a way for service workers to defer process until user is connected; however it can't be used for long tasks such as downloading a large file. The Background Sync requires that the service worker stays alive until the fetch is completed, and to conserve battery life and to prevent unwanted tasks happening in the background the browser will at some point terminate the task.
  • The Background Fetch API solves this problem. It creates a way for developers to tell the browser to perform some fetches in the background, for example when the user clicks a button to download a video file. The browser then performs the fetches in a user-visible way, displaying progress to the user and giving them a method to cancel the download. Once the download is complete the browser opens the service workers, at which point your application can do something with the response if required.
  • The Background Fetch API will enable the fetch to happen if the user starts the process while offline. Once they are connected it will begin. If the user goes offline, the process pauses until the user is on again.


Background Synchronization API


  • Background Synchronization API Mozilla Docs
  • Not available in Safari, so I'm not going to go into depth on it.
  • The Background Synchronization API enables a web app to defer tasks so that they can be run in a service worker once the user has a stable network connection.
  • The API allows web applications to defer server synchronization work to their service to handle at a later time, if the device is offline. Uses may include sending requests in the background if they couldn't be sent while the application was being used.


Content Index API


  • Content Index API Mozilla Docs
  • Not available in most browsers, so I'm not going to go into depth on it.
  • The API allows developers to register their offline enabled content with the browser.
  • Content indexing allows developers to tell the browser about their specific offline content.


Cookie Store API


  • Cookie Store API Mozilla Docs
  • Not available in Safari, so I'm not going to go into depth on it.
  • The Cookie Store API is an asynchronous API for managing cookies. The Cookie Store API provides an updated method for managing cookies. It is asynchronous and promise-based, therefore does not block the event loop.


Notifications API


The Notifications API allows web pages to control the display of system notifications to the end user. These are outside the top-level browsing context viewport, so therefore can be displayed even when the user has switched tabs or moved to a different app. The API is designed to be compatible with existing notification systems, across different platforms.
  • On supported platforms, showing a system notification generally involves two things. First, the user needs to grant the current origin permission to display system notifications, which is generally done when the app or site initializes, using the Notification.reequestPermission() method. This should be done in response to a user gesture.
  • A new notification is created using the Notification() constructor. This must be passed a title argument, and can optionally be passed an options object to specify options, such as text direction, body text, icon to display, notification sound to play, and more.


Payment Handler API


  • Payment Handler API Mozilla Docs
  • Not available in Safari, so I'm not going to go into depth on it.
  • This API provides a standardized set of functionality for web applications to directly handle payments, rather than having to be redirected to a separate site for payment handling. The Payment Handler API handles the discovery of applicable payment apps, presenting them as choice to the user, opening a payment handler window once a choice has been made to allow the user to enter their payment details, and handling the payment transaction with the payment app.
  • Communication via payment apps (authorization, passing of payment credentials) is handled via Service Workers.


Push API


The Push API gives web applications the ability to receive messages pushed to them from a server, whether or not the web app is in the foreground, or event loaded, on a user agent. This lets developers deliver asynchronous notifications and updates to users that opt in, resulting in better engagement with timely new content.
  • A service worker can subscribe to push notifications with PushManager.subscribe()
  • The resulting PushSubscription includes all the information that the application needs to send a push message: an endpoint and the encryption key needed for sending data.
  • The service worker will be started as necessary to handle incoming messages, which are delivered to the onpush event handler. This allows apps top react to push messages being received, for example, by displaying a notification (using ServiceWorkerRegistration.showNotification()).
  • Each subscription is unique to a service worker. The endpoint for the subscription is a unique capability URL: knowledge of the endpoint is all that is needed to send a message to your application. The endpoint URL needs to be kept secret, or other applications might be able to send push messages to your application.
  • Delivering push messages can increase battery resource usage. Some browsers implement limits on push notifications.


Web Periodic Background Synchronization API


  • Not available in Safari, so I'm not going to go into depth on it.
  • This API provides a way to register tasks to be run in a service worker at periodic intervals with network connectivity. These tasks are referred to as background sync requests.
  • The API allows web applications to alert their service worker to make any updates, at a periodic time interval. Uses may include fetching latest content while a device is connected to Wi-Fi, or allowing background updates to an application.


Comments

You must be logged in to post a comment!

Insert Math Markup

ESC
About Inserting Math Content
Display Style:

Embed News Content

ESC
About Embedding News Content

Embed Youtube Video

ESC
Embedding Youtube Videos

Embed TikTok Video

ESC
Embedding TikTok Videos

Embed X Post

ESC
Embedding X Posts

Embed Instagram Post

ESC
Embedding Instagram Posts

Insert Details Element

ESC

Example Output:

Summary Title
You will be able to insert content here after confirming the title of the <details> element.

Insert Table

ESC
Customization
Align:
Preview:

Insert Horizontal Rule

#000000

Preview:


Insert Chart

ESC

View Content At Different Sizes

ESC

Edit Style of Block Nodes

ESC

Edit the background color, default text color, margin, padding, and border of block nodes. Editable block nodes include paragraphs, headers, and lists.

#ffffff
#000000

Edit Selected Cells

Change the background color, vertical align, and borders of the cells in the current selection.

#ffffff
Vertical Align:
Border
#000000
Border Style:

Edit Table

ESC
Customization:
Align:

Upload Lexical State

ESC

Upload a .lexical file. If the file type matches the type of the current editor, then a preview will be shown below the file input.

Upload 3D Object

ESC

Upload Jupyter Notebook

ESC

Upload a Jupyter notebook and embed the resulting HTML in the text editor.

Insert Custom HTML

ESC

Edit Image Background Color

ESC
#ffffff

Insert Columns Layout

ESC
Column Type:

Select Code Language

ESC
Select Coding Language