Next.js & Mock Service Worker (MSW)

·

3 min read

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.