Service Worker Routing β Part 2: The Three-Strategy Router πΊοΈ
Service Worker Routing β Part 2: Auth-Aware Routing π
My notes from Kyle Simpson's "Exploring Service Workers" β Section 6, Lectures 6β9
Covers: Three-strategy router, auth-aware routing, login/logout handling, full demo
Lecture 6: Logged Out Routing Walkthrough πΊοΈ
This is the most important lecture in the section. Kyle pulls in a complete evolved router and walks through every decision. Three distinct resource types, three distinct strategies.
Resource Type 1: API Calls
if (reqURL.startsWith('/api')) {
if (isOnline) {
let fetchOptions = {
method: request.method,
headers: request.headers,
credentials: 'same-origin', // API calls need session cookies
cache: 'no-store'
};
let res = await fetch(request.url, fetchOptions);
if (res.ok) {
if (request.method === 'GET') { // never cache POST responses
await cache.put(reqURL, res.clone());
}
return res;
}
}
// Fallback: try cache
let res = await cache.match(reqURL);
if (res) return res.clone();
// Both failed β synthetic 404
return notFoundResponse();
}Key decisions:
credentials: 'same-origin'β unlike logged-out static file caching, API calls need session cookies. At least one endpoint (/api/add-post) requires authentication.- Only cache GET responses β POST responses should never be cached. The one GET API call fetches the list of blog post IDs for the homepage β worth caching for offline.
- Why cache API responses at all? The homepage won't show any posts if the API call fails. An old cached list is better than nothing.
- Synthetic
notFoundResponse()β fabricate a 404 so the page knows the AJAX failed cleanly:
function notFoundResponse() {
return new Response('', { status: 404, statusText: 'Not Found' });
}You can fabricate any response β status, headers, even a body. You're programming a proxy, you have complete control.
Resource Type 2: HTML Pages (Navigation Requests)
Detected via the Accept header β if a request includes text/html, it's a page navigation:
if (request.headers.get('Accept').includes('text/html')) {
// TODO: handle /login, /logout, /add-post separately (auth-sensitive)
if (isOnline) {
let res = await fetch(request.url, {
method: request.method,
headers: request.headers
// no credentials needed for public pages
});
if (res.ok) {
// Don't cache if server flagged it as a friendly 404
if (!res.headers.get('X-Not-Found')) {
await cache.put(reqURL, res.clone());
}
return res;
}
}
// Fallback: try cache
let res = await cache.match(reqURL);
if (res) return res.clone();
// Both failed β serve the offline page
return cache.match('/offline');
}Key decisions:
- Always try network first for HTML β page content changes. A typo fix on
/aboutshouldn't require bumping the SW version. Unlike CSS and JS where atomic updates make sense, HTML gets fresh-served whenever possible. - Detect HTML via
Acceptheader β more flexible than hard-coding a list of page URLs. Any request withtext/htmlin Accept is a page navigation. X-Not-Foundcustom header β the server returns a friendly 404 page with a200status (so the browser renders it nicely). But the SW must not cache it under the requested URL β that would pollute the cache with 404 content. The server appendsX-Not-Found: trueso the SW knows to show it but skip caching it.- Offline page as final fallback β if both network and cache fail, serve the pre-cached
/offlinepage. The message: "I can't tell if this page exists, I just can't reach it right now."
Resource Type 3: Static Assets (CSS, JS, Images)
// Cache first
let res = await cache.match(reqURL);
if (res) return res.clone();
// Not in cache β try network if online
if (isOnline) {
let fetchOptions = {
method: request.method,
headers: request.headers,
credentials: 'omit',
cache: 'no-store'
};
res = await fetch(request.url, fetchOptions);
if (res.ok) {
await cache.put(reqURL, res.clone());
return res;
}
}
// Both failed β 404 (no need for offline page for a missing stylesheet)
return notFoundResponse();Key decisions:
- Cache first β CSS, JS, images rarely change. If it's in cache, serve immediately with no network request.
- Only fetch if online β no point attempting a network request we know will fail.
- 404 not offline page β if CSS can't be loaded, the browser just gets a 404. No need to serve the offline HTML page for a missing stylesheet.
- Update cache on cache miss β if the asset wasn't cached and we're online, fetch it and store it for next time.
The Three-Strategy Summary
| Resource | Strategy | Final fallback |
|---|---|---|
| API calls | Network first (online only) β cache | Synthetic 404 |
| HTML pages | Network first (online only) β cache | Offline page |
| Static assets | Cache first β network (online only) | Synthetic 404 |
Workbox vs. Raw β The Trade-off
"Workbox makes a lot of these decisions for you. You make high-level declarative statements, like which strategy you want. But checking for a specific header from the server β that's not really a thing."
The X-Not-Found header check, selective caching of GET-only API responses, the different credentials handling per resource type β none of this is expressible in a high-level declarative framework. Raw service workers give you 100% control at the cost of 100% responsibility.
Next up: Authentication Aware Routing β handling the login, logout, and add-post routes that were left as TODOs.