Search code examples
javascriptes6-promiseservice-workerfetch-api

Uncaught DOMException (in promise) - Hijack specific GraphQL request in the serviceworker


I try to hijack a specific GraphQL-Request via the service-worker to fake the response from data in my IndexedDB, but I get an error that the event already has been responded to. The fetching is working for cached files, if the fetched data is not in the cache it will use the network. If there is no network there will be an offline fallback. How can I orchestrate my Promises that I can also hijack requests to my GraphQL API and a specific Query (operationName), because it seems i messed up with async event.respondWith calls ?

self.addEventListener('fetch', function (event) {
    if (event.request.url === __GRAPHQL_URL__) {
        event.request.clone().json().then(({operationName, variables}) => {
            switch (operationName) {
                case 'getOfflineFacilities':
                    //when a fetch is matching there will be the error
                    event.respondWith(serveOfflineFacilities());
            }
        });
    }else{

        event.respondWith(
            caches.match(event.request).then(function (response) {
                console.log("cache or network fallback");
                return response || fetch(event.request);
            }).catch(function () {
                console.log("offline fallback");
                return caches.match('/index.html');
            })
        );
    }
});

Error when there is a GraphQL Query that is hitting __GRAPHQL_URL__ and my operationName

sw.js:41 Uncaught (in promise) DOMException: Failed to execute 'respondWith' on 'FetchEvent': The event has already been responded to.


Solution

  • It's sparsely documented, but you need to call the respondWith method immediately in the handler. If the event handler exits and respondWith had not been called, the request will be handled so that the default response is served. respondWith will check the dispatch flag that is only set during the event handler call - when you only call it from a promise callback, you will get the "The event has already been responded to" exception.

    So you need to change your code to pass the whole promise to respondWith:

    if (event.request.url === __GRAPHQL_URL__) {
        event.respondWith(event.request.clone().json().then(({operationName, variables}) => {
    //  ^^^^^^^^^^^^^^^^^^
            switch (operationName) {
                case 'getOfflineFacilities':
                    return serveOfflineFacilities();
    //              ^^^^^^
            }
        }));
    //    ^
    }