-
-
Notifications
You must be signed in to change notification settings - Fork 517
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to deal with requests made before msw has been activated? #73
Comments
For anyone coming along, here's the basic gist of my current workaround: window.serverReady = start('/msw.js')
async function bootstrapAppData() {
await window.serverReady
await fetch('/something-that-should-be-mocked')
// etc...
} In production I don't think this should be a problem because you'd basically just await undefined which is nbd. |
I'd recommend to start the Service Worker as early as possible in your app. I usually put my import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import './mocks'
ReactDOM.render(<App />, rootElement)
However, since Service Worker activation is an async operation there is still a race condition in place of what happens first: your app's mount and potential data fetching, or MSW activation. To eliminate this race condition you can listen to the Promise returned from calling the // mocks.js
import { composeMocks } from 'msw'
return composeMocks(/* mock definition */) // index.js
import { start } from './mocks'
async function bootstrapApp() {
await start()
ReactDOM.render(<App />, rootElement)
}
bootstrapApp() It is up to the user to define at which level awaiting the |
Yep, I think that's the best that can be done here. Thanks. |
This should be re-opened. The promise returned by You can observe this in my app which has MSW deployed:
I've been able to workaround this by adding a timeout of 100ms before making any requests. I need some way to wait until the service worker has not only installed but is also ready to start proxying requests. |
@kentcdodds I think this has to do with the fact that Service Worker activation and activation of mocking are two separate subsequent events. The promise returned from This is doable, we just need to decide a proper way for that mocking activation to propagate to the client-side. Thanks for bumping this. |
Awesome! Can't wait to try this 👏 thanks! |
@kentcdodds, I plan to release it in Let me know if this changes resolves the activation Promise in time for you. |
Oh! I thought this was already released 😂 I already updated and removed my workaround. I guess I'll add it back for now 😅 |
Actually, from the look of my node modules I think this was already released 🤔 |
@kentcdodds should be present in |
Awesome 👍 I have 0.11.5 Thank you a lot! I'm giving a workshop right now using this. We haven't gotten to the data part yet, but I'm excited about it. Thank you! |
I'm glad to indirectly contribute to your workshop through MSW! I hope the workshop goes the best way. I've also noticed you've used some parts of the library in fetch-mock.js, which made me thinking whether MSW should expose some internals in a consumable way. If you have any thoughts on this, feel free to share. Thanks for using MSW. Means a lot. |
I would love it if some of the internals could be exposed. That would help 👍👍
…--- original message ---
On Tue, Mar 31 at 12:28 PM, <notifications@github.com> Artem Zakharchenko wrote:
I'm glad to indirectly contribute to your workshop through MSW! I hope the workshop goes the best way. I've also noticed you've used some parts of the library in fetch-mock.js, which made me thinking whether MSW should expose some internals in a consumable way. If you have any thoughts on this, feel free to share.
Thanks for using MSW. Means a lot.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or unsubscribe.
--- end of original message ---
|
Hi! I am following Kent Dodds' bookshelf app app and having troubles when the app tries to "bootstrap" a signed in user if they refresh the page. (It sends a request out to get the user's profile as soon as the app is mounted, like in Kent's bootstrap example.) I am firing up MSW in the app's index.js file
Then, I have an Here's what I have before any fetch requests are made: async function apiClient(endpoint, config) {
if (process.env.NODE_ENV === 'development') {
// If local development, wait for the MSW mock API to fire up
const registration = await window.__mockServerReady; // 👈wait for MSW to fire up
console.log(
'registration instanceof ServiceWorkerRegistration',
registration instanceof ServiceWorkerRegistration
);
console.log(JSON.stringify(window.__merchantMockServerReady));
}
// ... setup fetch request
console.log('prefetch', endpoint);
const res = await window.fetch(
`${API_URL_BASE}/${API_VERSION}/${endpoint}`,
config
);
console.log('postfetch', endpoint);
} And this is what I see in the console If I add a 500ms delay in the apiClient before the fetch, it works. async function apiClient(endpoint, config) {
if (process.env.NODE_ENV === 'development') {
// If local development, wait for the MSW mock API to fire up
const registration = await window.__mockServerReady;
console.log(
'registration instanceof ServiceWorkerRegistration',
registration instanceof ServiceWorkerRegistration
);
// 👇 Add this 500ms timeout and the initial "user session bootstrap" call works
if (!(registration instanceof ServiceWorkerRegistration)) {
await new Promise((r) => setTimeout(r, 500));
}
console.log(JSON.stringify(window.__merchantMockServerReady));
}
// ... setup fetch request
console.log('prefetch', endpoint);
const res = await window.fetch(
`${API_URL_BASE}/${API_VERSION}/${endpoint}`,
config
);
console.log('postfetch', endpoint);
} If I load the page so that it does not have the initial user session bootstrap call, it seems to load well Update I noticed that if I add an additional check to my bootstrap file before I make the call out to my apiClient it also works without the 500ms delay. if (process.env.NODE_ENV === 'development') {
// If local development, wait for the MSW mock API to fire up
await window.__mockServerReady;
} It made me realize that there is timing issue. I then moved the call to start bootstrapping inside my Kent has his bookshelf app work the first way, with the call outside the React component, but there is probably some subtle wizardry I'm missing to allow for that 😅 So, I found a workaround but not sure if my initial point of the /Update My apologies that this format for reporting the error isn't ideal but I haven't figured out how to create a codesandbox.com example with this lib yet. Please let me know if there's a more helpful format to report errors. Thanks again for your work on this library! I think it's really awesome! |
Hey, @mitchconquer. Please, don't worry, this format is perfectly fine. It's my bad, I haven't yet established a way to bootstrap scenarios with MSW in a sandbox form. Regarding the issue, I can recommend to store the start function itself, rather than its return statement. In case there's some dark magic in place, it'll help us ensure that we call the // mocks.js
const worker = setupWorker(...)
window.__mswStart = worker.start // notice the reference // apiClient.js
async function apiClient() {
if (__DEV__) {
await window.__mswStart()
}
} I'd be surprised if this didn't work. I have a few integration tests that rely on awaiting the Let me know if this suggestion is of any help. I'd prefer if you don't have to introduce timeouts to await the registration.
|
Hi! Thank you for the suggestion! I implemented it and see that the function was not set to the variable on I'm trying to find resources to explain why but in the meantime, requiring the MSW code at the top of my bootstrap file did the trick. Thanks again! 😸 |
To give an update: it does still fail if I hard refresh/reload but it works great otherwise. |
Anybody reading this now: MSW hijacks all requests that happen between calling |
@kettanaito Thanks for this awesome lib! I've been looking everywhere for how to get this running in a codesandbox. I've come up with this, but I feel like it's probably very brittle. Any guidance on the best implementation for this would be greatly appreciated. Also, please let me know if this isn't the right issue to comment on 😅 worker.start();
navigator.serviceWorker.register(
`${window.location.origin}/mockServiceWorker.js`
); Source sandbox: https://codesandbox.io/s/rtk-crud-entities-typed-kuwlp?file=/src/index.tsx |
Hey, @msutkowski. Thanks for the kind words! If you wish to change the Service Worker script URL, consider using the Last time I tried to set up a Codesandbox example I had an issue that it completely ignored a relative path to the worker, even if it's a valid path. The only way I got it to work is by supplying an absolute path to Please let me know if you get it to run in sandbox, we could use your experience to establish an official sandbox example. Thanks. |
Is this still true @kettanaito ? |
It's true but, apparently, we cannot account for all asynchronicity entirely. I recommend following the deferred mounting strategy for reliable results. We could also use some help debugging why some requests are not captured anyway, given we're using "deferNetworkRequest" function. |
I apologize to barge in like that, but I've been encountering this problem when using MSW with Vue 3 (+TypeScript). Also first of all I'd like to thank you @kettanaito for such an excellent and really easy-to-use non-intrusive backend-mock-library 😃. I am only using this for a home project to get to know Vue and Go (with only vague outcome set), but seeing how nice and easy, yet customizable it is, perhaps I should ask if we should give this a try at work, too, because using extensive proxy layers can be exhausting. But I digress... I initially was not able to await So I decided to do this instead: async function prepareWorkers(): Promise<void> {
if (process.env.NODE_ENV) {
await repositoryWorker.start({ ... });
}
}
prepareWorkers().then(() => {
createApp(App).mount("#app");
}); This basically allowed me to either start workers and wait for them to be started and mocking to become available (for development mode). If the software is running in any other environment, I'd expect It might also not be a good idea to completely ignore the Again: thank you very much! I am playing around with it. |
Thank you for your kind words, @divStar. Your approach makes sense, that's exactly what we suggest in the Deferred mounting recipe in our documentation.
I wouldn't handle any registration promise rejections. That way any rejection is propagated to the upper scope as an exception, failing the build. |
…tch if mocking is enabled so that MSW is initialised before live data fetching can occur. Common issue noted here that the app tends to mount before the MSW can be initialised: mswjs/msw#73 and vercel/next.js#39695. More info here -> https://mswjs.io/docs/integrations/browser "Because registering the Service Worker is an asynchronous operation, it’s a good idea to defer the rendering of your application until the registration Promise resolves." The example given in the MSW docs there conditionally renders the app element itself, but the timeout solution I've found works too and is less embedded in the code.
If I have a situation like this:
MSW will not be registered as ready until after the request has already been forwarded along. What do you recommend for situations like this where the app is trying to make requests immediately?
The text was updated successfully, but these errors were encountered: