Skip to content

Service Workers

Overview

Formbird uses service workers to cache static assets for offline use. The service worker is built using Workbox, a Google library that simplifies service worker development. Service workers enable the application to load and function even when the device is offline.

Architecture

Service Worker Files

File Purpose
ServiceWorker.ts Main service worker source (TypeScript)
serviceWorker.js Built service worker in server/public
ServiceWorkerStart.js Service worker registration and lifecycle
ServiceWorkerUpdater.js Updates service worker when config changes

Build Process

┌─────────────────────────────────────────────────────────────────────┐
│                      Build Pipeline                                  │
│                                                                      │
│  ServiceWorker.ts ──► Webpack ──► serviceWorker.js (unprocessed)    │
│                                          │                           │
│                                          ▼                           │
│                              Workbox injectManifest                  │
│                                          │                           │
│                                          ▼                           │
│                          serviceWorker.js (with manifest)            │
│                                                                      │
│  Manifest contains:                                                  │
│  - All static files (js, css, fonts, images)                        │
│  - Revision hashes for cache invalidation                           │
└─────────────────────────────────────────────────────────────────────┘

Workbox Configuration

Inject Manifest

The service worker uses Workbox's injectManifest to include the list of files to cache:

// gulp/tasks/service-worker.js
injectManifest({
    "swDest": swDest,
    "globDirectory": "server/public",
    "globPatterns": [
        "**/*.{gif,eot,svg,ttf,woff,woff2,ico,png,jpg,js,css,ijmap,html,wasm}"
    ],
    "globIgnores": [
        "vendor/custom-component-modules/**/*"
    ],
    "swSrc": "client/app/scripts/modules/core/serviceWorkers/ServiceWorker.js",
    "maximumFileSizeToCacheInBytes": 10 * 1024 * 1024  // 10mb
});

Glob Patterns

The globPatterns configuration determines which file types are cached:

Extension Type
gif, png, jpg, ico Images
js JavaScript bundles
css Stylesheets
woff, woff2, eot, ttf Fonts
html HTML pages
wasm WebAssembly modules
ijmap Image map files

Adding New File Types

To cache a new file type, add it to globPatterns:

"globPatterns": [
    "**/*.{gif,eot,svg,ttf,woff,woff2,ico,png,jpg,js,css,ijmap,html,wasm,newtype}"
]

Service Worker Lifecycle

Registration

The service worker is registered when the application loads:

// ServiceWorkerStart.js
if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/serviceWorker.js')
        .then(registration => {
            // Handle registration success
        })
        .catch(err => {
            // Handle registration failure
        });
}

States

State Description
Installing Service worker being installed
Installed Installation complete, waiting to activate
Activating Taking over from previous version
Activated Active and controlling pages
Redundant Replaced by newer version

Update Flow

1. Browser detects service worker file changed
2. New service worker starts installing
3. Old service worker continues serving requests
4. New service worker enters "waiting" state
5. When all tabs close, new version activates
6. App reloads with new service worker

Service Worker Updates

Automatic Updates

The service worker file is touched (modified timestamp updated) when:

  • Configuration document changes
  • Component is saved
  • Vendor library is updated
  • IndexedDB indexes change

This triggers the browser to check for updates.

ServiceWorkerUpdater

// ServiceWorkerUpdater.js
const serviceWorkerPath = './server/public/serviceWorker.js';

// Touch the file to trigger update
fs.utimesSync(serviceWorkerPath, new Date(), new Date());

Kubernetes Considerations

In Kubernetes deployments with multiple pods:

  • Service worker must be shared between all pods
  • Static files served from nginx (shared directory)
  • All instances update the same service worker file
# Pods share the same public directory
volumes:
  - name: public
    persistentVolumeClaim:
      claimName: web-app-public

Caching Strategies

Precaching (Static Assets)

Static assets are precached during service worker installation:

// Files listed in manifest are cached on install
workbox.precaching.precacheAndRoute(self.__WB_MANIFEST);

Runtime Caching (API Responses)

API responses can be cached at runtime:

// Cache-first for static data
workbox.routing.registerRoute(
    /\/api\/getFile\//,
    new workbox.strategies.CacheFirst()
);

// Network-first for dynamic data
workbox.routing.registerRoute(
    /\/api\/documents/,
    new workbox.strategies.NetworkFirst()
);

Vector Map Tile Caching

Map tiles are cached using runtime caching:

// Cache vector tiles from tile server
workbox.routing.registerRoute(
    new RegExp('https://vmap.formbird.com/data/'),
    new workbox.strategies.CacheFirst()
);

Vector Map Caching

Configuration

Configure tile server in clientConfiguration:

{
    "clientConfiguration": {
        "openLayers": {
            "tileServerGL": {
                "url": "https://vmap.formbird.com/data/v3/{z}/{x}/{y}.pbf"
            }
        }
    }
}

Supported Tile Servers

Server URL Pattern
tileserver-gl http://localhost:8080/data/v3/{z}/{x}/{y}.pbf
vmap.formbird.com https://vmap.formbird.com/data/v3/{z}/{x}/{y}.pbf
MapBox https://{a-d}.tiles.mapbox.com/v4/mapbox.streets-v6/{z}/{x}/{y}.vector.pbf
MapTiler https://api.maptiler.com/tiles/v3-openmaptiles/{z}/{x}/{y}.pbf

Caching Process

  1. User enables offline caching
  2. Service worker caches tiles for configured region
  3. Tiles stored in browser cache
  4. Map loads from cache when offline

Map Tile Storage Location

Map tiles are cached in "Cache Storage" under tiles-UUID, where UUID is unique for each device user. This allows multiple different Formbird logins to cache map tiles on the same browser without conflicts.

To view cached map tiles: 1. Open DevTools → Application → Cache Storage 2. Look for cache names starting with tiles-

Troubleshooting

Service Worker Not Installing

Symptoms: - White screen on load - App stuck at "Installing" - Service worker state stays at "installing"

Causes and Solutions:

Cause Solution
Slow connection Increase timeout or improve network
event.waitUntil blocking Check for stuck promises
Cache storage full Clear browser storage
HTTPS required Use localhost or HTTPS

Service Worker Not Updating

Symptoms: - Old version still showing after deploy - Changes not appearing

Solutions: 1. Hard refresh: Shift + Ctrl + R (Windows) or Shift + Cmd + R (Mac) 2. Clear site data in browser dev tools 3. Unregister service worker manually 4. Check service worker file changed on server

Files Not Cached

Symptoms: - Specific files fail to load offline - net::ERR_FAILED errors

Solutions: 1. Verify file type in globPatterns 2. Check file size under maximumFileSizeToCacheInBytes 3. Ensure file exists in server/public 4. Check for globIgnores excluding the file

WASM Files Not Working Offline

WebAssembly files need to be included in caching:

// Ensure wasm is in globPatterns
"globPatterns": [
    "**/*.{gif,eot,svg,ttf,woff,woff2,ico,png,jpg,js,css,ijmap,html,wasm}"
]

Version Mismatch Issues

The VersionService checks app version on startup:

// version.service.ts
if (appVersion !== serverAppVersion && appVersion !== undefined) {
    // Only unregister if appVersion was previously set
    await navigator.serviceWorker.getRegistration().then(reg => reg?.unregister());
}

Note: The appVersion !== undefined check prevents unregistering newly installed service workers on clean browsers.

Debugging

Chrome DevTools

  1. Open DevTools → Application tab
  2. Navigate to Service Workers section
  3. View registration status and logs
  4. Use "Update on reload" for development

Firefox DevTools

  1. Navigate to about:debugging#/runtime/this-firefox
  2. Find service worker in list
  3. Click "Inspect" to debug

Viewing Cached Files

  1. DevTools → Application → Cache Storage
  2. Expand cache names to see files
  3. Right-click to delete specific entries

Service Worker Console

// Enable verbose logging in service worker
Dexie.debug = true;
console.log('Service worker activating');