0
# Utility Functions
1
2
Helper functions for cache key management, request matching, and handler creation. These utilities provide access to precaching functionality without requiring a full controller instance.
3
4
## Capabilities
5
6
### Cache Key Management
7
8
Function for looking up cache keys for precached URLs.
9
10
```typescript { .api }
11
/**
12
* Takes a URL and returns the corresponding cache key used in the precache.
13
* Handles revision parameters automatically.
14
* @param url - The URL to look up the cache key for
15
* @returns The cache key or undefined if not found
16
*/
17
function getCacheKeyForURL(url: string): string | undefined;
18
```
19
20
**Usage Examples:**
21
22
```typescript
23
import { getCacheKeyForURL, precacheAndRoute } from "workbox-precaching";
24
25
// First precache some assets
26
precacheAndRoute([
27
{ url: "/index.html", revision: "abc123" },
28
"/logo.png", // No revision, uses URL as cache key
29
]);
30
31
// Later, look up cache keys
32
const indexCacheKey = getCacheKeyForURL("/index.html");
33
console.log(indexCacheKey); // "/index.html?__WB_REVISION__=abc123"
34
35
const logoCacheKey = getCacheKeyForURL("/logo.png");
36
console.log(logoCacheKey); // "/logo.png"
37
38
const notFoundKey = getCacheKeyForURL("/nonexistent.html");
39
console.log(notFoundKey); // undefined
40
```
41
42
### Request Matching
43
44
Function for looking up requests in the precache without needing a controller instance.
45
46
```typescript { .api }
47
/**
48
* Helper function that looks up a request in the precache.
49
* This is a wrapper around PrecacheController#matchPrecache using the default controller.
50
* @param request - The key (URL string or Request object) to look up
51
* @returns Promise resolving to cached response or undefined
52
*/
53
function matchPrecache(request: string | Request): Promise<Response | undefined>;
54
```
55
56
**Usage Examples:**
57
58
```typescript
59
import { matchPrecache, precacheAndRoute } from "workbox-precaching";
60
61
// Precache assets first
62
precacheAndRoute([
63
{ url: "/offline.html", revision: "v1.0" },
64
{ url: "/app-shell.html", revision: "v2.1" }
65
]);
66
67
// In service worker fetch event
68
self.addEventListener('fetch', async (event) => {
69
// Try to match against precache
70
const cachedResponse = await matchPrecache(event.request);
71
72
if (cachedResponse) {
73
console.log('Serving from precache:', event.request.url);
74
event.respondWith(cachedResponse);
75
return;
76
}
77
78
// Fallback to network
79
event.respondWith(fetch(event.request));
80
});
81
82
// Can also use with URL strings
83
const offlineResponse = await matchPrecache("/offline.html");
84
if (offlineResponse) {
85
// Use the cached offline page
86
return offlineResponse;
87
}
88
89
// Handle Request objects
90
const request = new Request("/app-shell.html");
91
const shellResponse = await matchPrecache(request);
92
```
93
94
### Handler Creation
95
96
Function for creating route handlers bound to specific precached URLs.
97
98
```typescript { .api }
99
/**
100
* Helper function that creates a route handler bound to a specific precached URL.
101
* Returns a handler function that serves the specified precached resource.
102
* @param url - The precached URL to bind the handler to
103
* @returns Route handler function that serves the precached resource
104
* @throws Will throw if the URL is not in the precache
105
*/
106
function createHandlerBoundToURL(url: string): RouteHandlerCallback;
107
```
108
109
**Usage Examples:**
110
111
```typescript
112
import {
113
createHandlerBoundToURL,
114
precacheAndRoute
115
} from "workbox-precaching";
116
import { registerRoute } from "workbox-routing";
117
118
// Precache assets first
119
precacheAndRoute([
120
{ url: "/", revision: "v1.0" },
121
{ url: "/offline.html", revision: "v1.1" },
122
{ url: "/app-shell.html", revision: "v2.0" }
123
]);
124
125
// Create handlers for specific URLs
126
const offlineHandler = createHandlerBoundToURL("/offline.html");
127
const shellHandler = createHandlerBoundToURL("/app-shell.html");
128
129
// Use handlers with routing
130
registerRoute(
131
// Serve offline page for failed navigation requests
132
({ request, event }) => {
133
return request.mode === 'navigate' && !navigator.onLine;
134
},
135
offlineHandler
136
);
137
138
registerRoute(
139
// Serve app shell for SPA routes
140
({ request, url }) => {
141
return request.mode === 'navigate' &&
142
url.pathname.startsWith('/app/');
143
},
144
shellHandler
145
);
146
147
// Direct usage in fetch event
148
self.addEventListener('fetch', (event) => {
149
if (event.request.mode === 'navigate' && event.request.url.includes('/dashboard')) {
150
// Always serve app shell for dashboard routes
151
const handler = createHandlerBoundToURL("/app-shell.html");
152
event.respondWith(handler({ request: event.request, event }));
153
}
154
});
155
```
156
157
## Advanced Usage Patterns
158
159
### Fallback Chain
160
161
```typescript
162
import {
163
matchPrecache,
164
createHandlerBoundToURL,
165
precacheAndRoute
166
} from "workbox-precaching";
167
168
// Precache fallback resources
169
precacheAndRoute([
170
{ url: "/offline.html", revision: "v1.0" },
171
{ url: "/fallback.css", revision: "v1.0" },
172
{ url: "/404.html", revision: "v1.0" }
173
]);
174
175
// Create fallback handlers
176
const offlineHandler = createHandlerBoundToURL("/offline.html");
177
const notFoundHandler = createHandlerBoundToURL("/404.html");
178
179
self.addEventListener('fetch', async (event) => {
180
// First, try precache
181
const cachedResponse = await matchPrecache(event.request);
182
if (cachedResponse) {
183
event.respondWith(cachedResponse);
184
return;
185
}
186
187
// Then try network
188
event.respondWith(
189
fetch(event.request)
190
.catch(async (error) => {
191
// Network failed, use appropriate fallback
192
if (event.request.mode === 'navigate') {
193
return offlineHandler({ request: event.request, event });
194
}
195
196
if (event.request.destination === 'document') {
197
return notFoundHandler({ request: event.request, event });
198
}
199
200
// For other resources, try to find a similar precached resource
201
return matchPrecache('/fallback.css') || new Response('Offline', {
202
status: 503,
203
statusText: 'Service Unavailable'
204
});
205
})
206
);
207
});
208
```
209
210
### Cache Key Validation
211
212
```typescript
213
import { getCacheKeyForURL, precacheAndRoute } from "workbox-precaching";
214
215
// Precache with revisions
216
precacheAndRoute([
217
{ url: "/critical.css", revision: "v1.5.2" },
218
{ url: "/app.js", revision: "v2.1.0" }
219
]);
220
221
// Utility to check if resources are properly cached
222
function validatePrecacheIntegrity(expectedAssets: Record<string, string>) {
223
const issues: string[] = [];
224
225
for (const [url, expectedRevision] of Object.entries(expectedAssets)) {
226
const cacheKey = getCacheKeyForURL(url);
227
228
if (!cacheKey) {
229
issues.push(`Missing from precache: ${url}`);
230
continue;
231
}
232
233
// Extract revision from cache key
234
const revisionMatch = cacheKey.match(/\?__WB_REVISION__=(.+)$/);
235
const actualRevision = revisionMatch ? revisionMatch[1] : 'none';
236
237
if (actualRevision !== expectedRevision) {
238
issues.push(`Version mismatch for ${url}: expected ${expectedRevision}, got ${actualRevision}`);
239
}
240
}
241
242
return issues;
243
}
244
245
// Check integrity
246
const issues = validatePrecacheIntegrity({
247
'/critical.css': 'v1.5.2',
248
'/app.js': 'v2.1.0'
249
});
250
251
if (issues.length > 0) {
252
console.warn('Precache integrity issues:', issues);
253
}
254
```
255
256
### Dynamic Handler Selection
257
258
```typescript
259
import {
260
createHandlerBoundToURL,
261
getCacheKeyForURL,
262
precacheAndRoute
263
} from "workbox-precaching";
264
import { registerRoute } from "workbox-routing";
265
266
// Precache multiple app shells
267
precacheAndRoute([
268
{ url: "/mobile-shell.html", revision: "v1.0" },
269
{ url: "/desktop-shell.html", revision: "v1.0" },
270
{ url: "/tablet-shell.html", revision: "v1.0" }
271
]);
272
273
// Dynamic handler based on user agent
274
const createResponsiveHandler = () => {
275
const mobileHandler = createHandlerBoundToURL("/mobile-shell.html");
276
const desktopHandler = createHandlerBoundToURL("/desktop-shell.html");
277
const tabletHandler = createHandlerBoundToURL("/tablet-shell.html");
278
279
return async ({ request, event }) => {
280
const userAgent = request.headers.get('user-agent') || '';
281
282
if (/Mobile/.test(userAgent)) {
283
return mobileHandler({ request, event });
284
} else if (/Tablet|iPad/.test(userAgent)) {
285
return tabletHandler({ request, event });
286
} else {
287
return desktopHandler({ request, event });
288
}
289
};
290
};
291
292
// Register responsive route
293
registerRoute(
294
({ request }) => request.mode === 'navigate',
295
createResponsiveHandler()
296
);
297
```
298
299
### Conditional Precache Serving
300
301
```typescript
302
import { matchPrecache, getCacheKeyForURL } from "workbox-precaching";
303
304
// Check if resource is precached before serving
305
async function servePrecachedIfAvailable(request: Request): Promise<Response> {
306
const url = new URL(request.url);
307
308
// Check if we have this resource precached
309
const cacheKey = getCacheKeyForURL(url.pathname);
310
if (!cacheKey) {
311
throw new Error(`Resource not precached: ${url.pathname}`);
312
}
313
314
// Try to serve from precache
315
const cachedResponse = await matchPrecache(request);
316
if (cachedResponse) {
317
// Add custom headers to indicate precached response
318
const response = new Response(cachedResponse.body, {
319
status: cachedResponse.status,
320
statusText: cachedResponse.statusText,
321
headers: {
322
...cachedResponse.headers,
323
'X-Served-From': 'precache',
324
'X-Cache-Key': cacheKey
325
}
326
});
327
return response;
328
}
329
330
throw new Error(`Failed to retrieve precached resource: ${url.pathname}`);
331
}
332
```