In frontend development, we often need to build features even when the backend API isn’t ready.
By leveraging Service Workers and MSW (Mock Service Worker), you can set up a fully functional frontend environment without relying on a real API.
A Service Worker is a JavaScript file that runs in the browser’s background, acting as a proxy between the client and server.
Using a service worker, you can intercept network requests, cache responses, and even make your app work offline.
In practice, it’s useful for handling mock data as if it were coming from a real API, allowing frontend development to proceed even before the BFF server is ready.
The tool commonly used for this is MSW (Mock Service Worker).
MSW registers a service worker that intercepts browser API requests and returns mock data.
Suppose you have a component using useQuery:
// Component code
const { data } = useQuery({
queryKey: ['...'],
queryFn: getData,
})
// getData function
const getData = async () => {
await fetch(dummyURL)
}
}
Here, dummyURL is a placeholder for testing. Without MSW, this component won’t work locally.
MSW intercepts requests to dummyURL and returns mock data, allowing your component to function normally.
export const getSuccessMockData = (data: WLResponseListCardsResponse) => {
return http.get(dummyURL, () => {
return new HttpResponse(JSON.stringify(data), { status: 200 })
})
}
export const getErrorMockData = () => {
return http.get(dummyURL, () => HttpResponse.error())
}
export const getLoadingMockData = () => {
return http.get(dummyURL, async () => {
await delay(99999) // simulate loading state
})
}
getErrorMockData and getLoadingMockData can be applied in Storybook decorators using worker.use().
pnpm dlx msw init public/ --save
public/mockServiceWorker.jsdummyURL requests:import { setupWorker } from 'msw'
const worker = setupWorker()
worker.start()
Use Storybook args and decorators to dynamically control mock API responses.
initialize({
serviceWorker: { url: '/mockServiceWorker.js' }
})
argTypes: {
[dummyURL]: { control: 'object' },
},
args: {
[dummyURL]: getSuccessMockData,
},
decorators: [(Story, ctx) => {
worker.resetHandlers(ctx.args.dummyURL)
return <Story />
}],
argsresetHandlers() to intercept requests to dummyURL