Not sure if it is even a msw problem, but maybe someone knows the answer. For some reason, when msw is enabled, I can not set up a filter to see only XHR requests in Chrome network tab. See how it looks like below (note, that static files are displayed even though the XHR filter is on.)
msw: 0.21.2nodejs: 12.16.1npm: 6.14.8Chrome Version 85.0.4183.121 (Official Build) (64-bit)Steps to reproduce the behavior:
Only XHR requests are displayed

Hi @malykhinvi thanks for reaching us 馃槃 .
I have to be honest, I didn't notice that 馃槅 . BTW I can say that Service Workers are like a proxy. So in your DevTools you should see requests two times. I'm pretty sure it is a normal. You should consider also that the Service Workers will listen for fetch events. Also static files are intercepted by worker, but for those files the request is not forwarded to MSW
Just installed msw and it's the first thing I noticed. Static assets are displayed twice, once where they should and once as XHR requests. It's causing some confusion.
That's the behavior caused by the current implementation of request handling in the worker. As the worker persists between pages, when the app loads the worker handles _all_ requests. Since you rarely want to mock a static assets (which may render your site broken), the worker skips such requests and _performs then as usual_.
We can get rid of such requests duplication if it's possible to short-circuit a request handling in the fetch event of the Service Worker. So that we don't return anything in case of a static asset, stating that such request should be performed as is (outside of the worker).
It seems that short circuiting within the fetch event handler function in the worker bypasses the request (it gets performed as-is) without it displaying in the XHR tab. I've issued a pull request with the fix, expect it in the nearest future.
Although I see a problem in Chrome dev tools, this is not the case for Firefox. It filters XHR requests just fine. Maybe it is something with dev tools?
Service Worker implementation and thus behavior may differ in different browsers. There's nothing we can or should do about this, unfortunately.
During the work on this I've found out a few details that I'd like to share.
No, this won't be possible to do due to the Service Worker specification.
The worker's "fetch" event triggers for any HTTP request from the client once the worker is active. The fetch event handler is a _synchronous function_, which may call event.respondWith() to respond with any Response, but it must call it synchronously:
self.addEventListener('fetch', function (event) {
event.respondWith(anyPromise) // OK
await somePromise()
event.respondWith(...) // INVALID STATE
})
So there's the first behavior we learn:
event.respondWith() must be called synchronously in the "fetch" event handler.To fetch data in this worker's event handler you can use the fetch() API. Since it's async, we can only use it _within_ the respondWith() Promise:
event.respondWith(async () => {
return fetch('...').then(data => data.json())
})
Whenever you perform a fetch() call within the worker's scope it gets registered as the XHR in the Network tab. That is the behavior one cannot change. Thus, the next discovery:
fetch() calls in the worker's scope are always registered as XHRNow let's analyze what happens to a request. Specifically, to a static asset request that shouldn't be mocked. Here's the journey it undergoes:
event.respondWith(fetch(event.request)).Since the step 5 is async, it must be nested in the event.respondWith() promise. If the request shouldn't be mocked, to perform an original request one needs to call fetch(event.request), which always produces an XHR request. This means that the requests that should be bypassed are performed as XHR and you can see them twice in your browser's Network tab.
There are much more nuances to the async worker behavior. For instance, there is a time window between steps 2 and 3 and if a request happens there it's _always_ bypassed, as that means "the worker is not ready to signal to MSW". _Those_ requests will not be listed as XHR, because the fetch event handler short circuits on those requests _outside_ of the
event.respondWith()function call.
If this request duplication in the Network is confusing, this is how you can think of it:
@kettanaito thanks for your investigation and for a very detailed response! Do you have any idea, why it is possible to filter XHR requests in FF dev tools?
As I've mentioned, Service Worker implementation may differ in browsers. I suppose Firefox can understand that this XHR originates from the worker, and hides it in the list of all XHR. Chrome can understand that as well, but doesn't hide those requests. I support Chrome's behavior here, as the worker indeed performs an extra request to use for the actual response, thus it makes sense to list that extra request.
Really good explanation, it makes sense now. Thank you!
I'm closing the issue because there isn't much we can do on this behavior. The PR that slightly improves the requests bypassing will land in the next minor version. Thanks for raising this!
Most helpful comment
During the work on this I've found out a few details that I'd like to share.
Short answer
No, this won't be possible to do due to the Service Worker specification.
Long answer
The worker's "fetch" event triggers for any HTTP request from the client once the worker is active. The fetch event handler is a _synchronous function_, which may call
event.respondWith()to respond with anyResponse, but it must call it synchronously:So there's the first behavior we learn:
event.respondWith()must be called synchronously in the "fetch" event handler.To fetch data in this worker's event handler you can use the
fetch()API. Since it's async, we can only use it _within_ therespondWith()Promise:Whenever you perform a
fetch()call within the worker's scope it gets registered as the XHR in the Network tab. That is the behavior one cannot change. Thus, the next discovery:fetch()calls in the worker's scope are always registered as XHRNow let's analyze what happens to a request. Specifically, to a static asset request that shouldn't be mocked. Here's the journey it undergoes:
event.respondWith(fetch(event.request)).Since the step 5 is async, it must be nested in the
event.respondWith()promise. If the request shouldn't be mocked, to perform an original request one needs to callfetch(event.request), which always produces an XHR request. This means that the requests that should be bypassed are performed as XHR and you can see them twice in your browser's Network tab.If this request duplication in the Network is confusing, this is how you can think of it: