@@ -286,6 +286,50 @@ const deviceAwareFetch = createFetch(await caches.open('content-cache'), {
286286const response = await deviceAwareFetch (' /api/content' );
287287```
288288
289+ ### Custom Fetch with Authentication
290+
291+ ``` typescript
292+ // Production-ready example with automatic token refresh
293+ const createAuthenticatedFetch = (getToken ) => {
294+ return async (input , init ) => {
295+ const token = await getToken ();
296+ const headers = new Headers (init ?.headers );
297+ headers .set (' Authorization' , ` Bearer ${token } ` );
298+
299+ const response = await globalThis .fetch (input , {
300+ ... init ,
301+ headers
302+ });
303+
304+ // Handle token expiration
305+ if (response .status === 401 ) {
306+ // Token might be expired, retry once with fresh token
307+ const freshToken = await getToken (true ); // force refresh
308+ headers .set (' Authorization' , ` Bearer ${freshToken } ` );
309+
310+ return globalThis .fetch (input , {
311+ ... init ,
312+ headers
313+ });
314+ }
315+
316+ return response ;
317+ };
318+ };
319+
320+ const authFetch = createFetch (await caches .open (' authenticated-api' ), {
321+ fetch: createAuthenticatedFetch (() => getApiToken ()),
322+ defaults: {
323+ cacheControlOverride: ' s-maxage=300' ,
324+ cacheKeyRules: {
325+ header: { include: [' authorization' ] } // Cache per token
326+ }
327+ }
328+ });
329+
330+ const userData = await authFetch (' /api/user/profile' );
331+ ```
332+
289333## 🌐 Global Setup
290334
291335### Setting up Global Cache Storage
@@ -376,6 +420,71 @@ const response2 = await fetch('/api/data', {
376420});
377421```
378422
423+ ### Custom Fetch Configuration
424+
425+ The ` createFetch ` function accepts a custom fetch implementation, allowing you to integrate with existing HTTP clients or add cross-cutting concerns:
426+
427+ ``` typescript
428+ // Example: Integration with axios
429+ import axios from ' axios' ;
430+
431+ const axiosFetch = async (input , init ) => {
432+ const response = await axios ({
433+ url: input .toString (),
434+ method: init ?.method || ' GET' ,
435+ headers: init ?.headers ,
436+ data: init ?.body ,
437+ validateStatus : () => true , // Don't throw on 4xx/5xx
438+ });
439+
440+ return new Response (response .data , {
441+ status: response .status ,
442+ statusText: response .statusText ,
443+ headers: response .headers ,
444+ });
445+ };
446+
447+ const fetch = createFetch (await caches .open (' axios-cache' ), {
448+ fetch: axiosFetch ,
449+ defaults: {
450+ cacheControlOverride: ' s-maxage=300'
451+ }
452+ });
453+
454+ // Example: Custom fetch with request/response transformation
455+ const transformFetch = async (input , init ) => {
456+ // Transform request
457+ const url = new URL (input );
458+ url .searchParams .set (' timestamp' , Date .now ().toString ());
459+
460+ const response = await globalThis .fetch (url , init );
461+
462+ // Transform response
463+ if (response .headers .get (' content-type' )?.includes (' application/json' )) {
464+ const data = await response .json ();
465+ const transformedData = {
466+ ... data ,
467+ fetchedAt: new Date ().toISOString ()
468+ };
469+
470+ return new Response (JSON .stringify (transformedData ), {
471+ status: response .status ,
472+ statusText: response .statusText ,
473+ headers: response .headers ,
474+ });
475+ }
476+
477+ return response ;
478+ };
479+
480+ const transformedFetch = createFetch (await caches .open (' transform-cache' ), {
481+ fetch: transformFetch ,
482+ defaults: {
483+ cacheControlOverride: ' s-maxage=300'
484+ }
485+ });
486+ ```
487+
379488### Enhanced Fetch API
380489
381490SharedCache extends the standard fetch API with caching options via the ` sharedCache ` parameter:
@@ -678,7 +787,7 @@ function createFetch(
678787** Parameters :**
679788
680789- ` cache ` - Optional SharedCache instance (auto - discovered from globalThis .caches if not provided )
681- - ` options.fetch ` - Custom fetch implementation to use as the underlying fetcher
790+ - ` options.fetch ` - Custom fetch implementation to use as the underlying fetcher ( defaults to globalThis . fetch )
682791- ` options.defaults ` - Default shared cache options to apply to all requests
683792
684793** Default Options :**
@@ -706,6 +815,112 @@ const fetch = createFetch(await caches.open('my-cache'), {
706815});
707816` ` `
708817
818+ #### Custom Fetch Implementation
819+
820+ The ` options.fetch ` parameter allows you to provide a custom fetch implementation , enabling you to :
821+
822+ - ** Add authentication ** : Automatically include API keys or tokens
823+ - ** Implement retries ** : Add retry logic for failed requests
824+ - ** Custom headers ** : Add default headers to all requests
825+ - ** Request / response transformation ** : Modify requests or responses
826+ - ** Logging and monitoring ** : Add request / response logging
827+
828+ ** Custom Fetch Examples :**
829+
830+ ` ` ` typescript
831+ // Example 1: Fetch with automatic authentication
832+ const authenticatedFetch = async (input, init) => {
833+ const headers = new Headers(init?.headers);
834+ headers.set('Authorization', ` Bearer $ {getApiToken ()}` );
835+
836+ return globalThis.fetch(input, {
837+ ...init,
838+ headers
839+ });
840+ };
841+
842+ const fetch = createFetch(await caches.open('auth-cache'), {
843+ fetch: authenticatedFetch,
844+ defaults: {
845+ cacheControlOverride: 's-maxage=300'
846+ }
847+ });
848+
849+ // Example 2: Fetch with retry logic and logging
850+ const retryFetch = async (input, init, maxRetries = 3) => {
851+ let lastError;
852+
853+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
854+ try {
855+ console.log( ` Attempt $ {attempt }: $ {init ?.method || ' GET' } $ {input }` );
856+ const response = await globalThis.fetch(input, init);
857+
858+ if (response.ok || attempt === maxRetries) {
859+ return response;
860+ }
861+
862+ lastError = new Error( ` HTTP $ {response .status }: $ {response .statusText }` );
863+ } catch (error) {
864+ lastError = error;
865+ if (attempt === maxRetries) break;
866+
867+ // Exponential backoff
868+ await new Promise(resolve =>
869+ setTimeout(resolve, Math.pow(2, attempt - 1) * 1000)
870+ );
871+ }
872+ }
873+
874+ throw lastError;
875+ };
876+
877+ const resilientFetch = createFetch(await caches.open('resilient-cache'), {
878+ fetch: retryFetch,
879+ defaults: {
880+ cacheControlOverride: 's-maxage=600'
881+ }
882+ });
883+
884+ // Example 3: Fetch with custom base URL and headers
885+ const createApiFetch = (baseUrl, defaultHeaders = {}) => {
886+ return async (input, init) => {
887+ const url = new URL(input, baseUrl);
888+ const headers = new Headers(init?.headers);
889+
890+ // Add default headers
891+ Object.entries(defaultHeaders).forEach(([key, value]) => {
892+ if (!headers.has(key)) {
893+ headers.set(key, value);
894+ }
895+ });
896+
897+ return globalThis.fetch(url.toString(), {
898+ ...init,
899+ headers
900+ });
901+ };
902+ };
903+
904+ const apiFetch = createFetch(await caches.open('api-cache'), {
905+ fetch: createApiFetch('https://api.example.com', {
906+ 'Content-Type': 'application/json',
907+ 'X-API-Version': '2024-01-01'
908+ }),
909+ defaults: {
910+ cacheControlOverride: 's-maxage=300'
911+ }
912+ });
913+
914+ // Usage: relative URLs are automatically resolved
915+ const userData = await apiFetch('/users/me'); // → https://api.example.com/users/me
916+ ` ` `
917+
918+ ** Custom Fetch Requirements :**
919+
920+ - Must be compatible with the standard fetch API signature : ` (input: RequestInfo | URL, init?: RequestInit) => Promise<Response> `
921+ - Should handle errors appropriately and return valid Response objects
922+ - Response objects should be consumable by SharedCache (cloneable for caching )
923+
709924### Internal Implementation
710925
711926The ` createFetch ` function is the primary API for creating cached fetch functions, but the package exports many additional utilities and classes for comprehensive cache management.
0 commit comments