Next.js & Mock Service Worker (MSW)
Introduction
MSW supports both mockings from the server and mockings from the client.
Let me show you an example.
Folder and File Structure
public
msw-mock
|-browser.ts
|-server.ts
|-types.ts
pages
|-client.tsx
|-server.tsx
Installation
$ npm install msw --save-dev
For the Client Side, an Additional File Is Needed.
$ npx msw init public/ --save
If you check the 'public' folder, 'mockServiceWorker.js' file will be seen.
Configuration
|tsconfig.json|
"compilerOptions": {
"baseUrl": "."
}
Codes
|msw-mock/browser.ts|
import { rest, setupWorker } from 'msw';
import { Data } from 'msw-mock/types';
const handler1 = rest.get('/from-frontend-1', (_req, res, ctx) => {
return res(
ctx.json<Data>({
title: 'title 1',
description: 'description 1',
})
);
});
const handler2 = rest.get('/from-frontend-2', (_req, res, ctx) => {
return res(
ctx.json<Data>({
title: 'title 2',
description: 'description 2',
})
);
});
export const worker = setupWorker(handler1, handler2);
|msw-mock/server.ts|
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import { Data } from './types';
const handler1 = rest.get('https://from-backend-1.com', (_req, res, ctx) => {
return res(
ctx.json<Data>({
title: 'title 1',
description: 'description 1',
})
);
});
const handler2 = rest.get('https://from-backend-2.com', (_req, res, ctx) => {
return res(
ctx.json<Data>({
title: 'title 2',
description: 'description 2',
})
);
});
export const server = setupServer(handler1, handler2);
|msw-mock/types.ts|
export type Data = {
title: string;
description: string;
};
|pages/client.tsx|
import { useEffect, useState } from 'react';
import { Data } from 'msw-mock/types';
export default function () {
const [data1, setData1] = useState<Data | null>(null);
const [data2, setData2] = useState<Data | null>(null);
useEffect(() => {
(async () => {
// turning on the mock server
// Be careful not to visit before the mock server is turned on.
const { worker } = await import('msw-mock/browser');
worker.start();
// getting the data
const res1 = await fetch('/from-frontend-1');
const data1 = await res1.json();
setData1(data1);
const res2 = await fetch('/from-frontend-2');
const data2 = await res2.json();
setData2(data2);
})();
}, []);
return (
<div>
<h1>Data 1</h1>
{data1 && (
<>
<p>{data1.title}</p>
<p>{data1.description}</p>
</>
)}
<h1>Data 2</h1>
{data2 && (
<>
<p>{data2.title}</p>
<p>{data2.description}</p>
</>
)}
</div>
);
}
|pages/server.tsx|
import { Data } from 'msw-mock/types';
type Props = {
data: Data[];
};
export default function ({ data }: Props) {
return (
<div>
<h1>Data 1</h1>
<p>{data[0].title}</p>
<p>{data[0].description}</p>
<h1>Data 2</h1>
<p>{data[1].title}</p>
<p>{data[1].description}</p>
</div>
);
}
export async function getServerSideProps() {
// turning on the mock server
const { server } = await import('msw-mock/server');
server.listen();
// getting the data
const res1 = await fetch('https://from-backend-1.com');
const data1 = await res1.json();
const res2 = await fetch('https://from-backend-2.com');
const data2 = await res2.json();
const data = [data1, data2];
return {
props: {
data,
},
};
}
Result
If you visit ~/client or ~/server, this screen will be shown.