I am currently developing a front-end application in Nextjs, using RTK Query for API fetching and use MSW for its mock.
If mockServiceWorker.js is already loaded at the time the page is loaded, the API mock will be intercepted without problems. However, when the page is reloaded from the browser, the API query is executed first and the API is called before msw catches it, resulting in a 500 error.
I struggle with controlling when RTK query is called and I have also looked into when public files like mockServiceWorker.js are loaded, etc., but have not reached to find a solution.
Here is the sample page code:
src/pages/_app.tsx
import React, { useEffect } from 'react';
import { CacheProvider, EmotionCache } from '@emotion/react';
import { AppProps } from 'next/app';
import Head from 'next/head';
import { GlobalLayout } from '../components/globals/GlobalLayout';
import { Providers } from '../components/globals/Providers';
import createEmotionCache from '../createEmotionCache';
import '../styles/globals.css';
if (process.env.NODE_ENV !== 'production') {
if (process.env.NEXT_PUBLIC_ENABLE_MOCK_SERVER) {
const { worker } = require('./mocks/browser')
worker.start()
}
}
// Client-side cache, shared for the whole session of the user in the browser.
const clientSideEmotionCache = createEmotionCache();
export interface MyAppProps extends AppProps {
emotionCache?: EmotionCache;
}
export default function MyApp(props: MyAppProps) {
const { Component, emotionCache = clientSideEmotionCache, pageProps } = props;
useEffect(() => {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles) {
jssStyles.parentElement?.removeChild(jssStyles);
}
}, []);
return (
<CacheProvider value={emotionCache}>
<Head>
<title>Front Template</title>
<meta name="viewport" content="initial-scale=1, width=device-width" />
<meta name="description" content="Front Template" />
</Head>
<Providers>
<GlobalLayout>
<Component {...pageProps} />
</GlobalLayout>
</Providers>
</CacheProvider>
);
}
src/pages/test.tsx
export default function Test() {
const [fetch, { data }] = usePostApiFooMutation();
useEffect(() => {
fetch(args);
}, [fetch, args]);
return (
<>
<Button onClick={() => fetch(args)}>fetch</Button>
<div>{JSON.stringify(data?.payload)}</div>
</>
);
}
environment:
- typescript:4.5.2
- react:18.2.0
- next:13.4.3
- reduxjs/toolkit:1.9.5
- msw:1.2.1
2
Answers
The following link has an answer to a similar problem. MSW(Mock Service Worker) in Next js first render not working
I added the same code to monitor the worker startup before mounting the application and it works fine.
I believe this issue happens because registering (and activating) a Service Worker is an asynchronous action. This creates a race condition between your app rendering and making the initial request, and MSW activating.
To tackle this, defer your application’s mounting until the worker is ready to process requests. We mention an example of how to do this in the Deferred mounting recipe in the documentation. Hope this helps.