Mocking API calls using MSW in Next.js and React

ยท

4 min read

What is MSW

MSW stands for Mock Service Worker, which as per the offical docs, is defined as "Mock Service Worker is an API mocking library that allows you to write client-agnostic mocks and reuse them across any frameworks, tools, and environments."

This article will cover, how to set up MSW in both Next.js and React as well as how to mock API calls using it.

Setting up MSW in Next.js

  1. Install MSW in your project, for this article we'll be using pnpm but you use any package manager you want
pnpm add msw -D
  1. Now we have to intialize mockServiceWorker.js file in our public directory. To do that run the following command in your terminal
npx msw init <PUBLIC_DIR> --save
  1. Once you run the above command you can see the mockServiceWorker.js file created in your public directory. Now we need to define a few more files, which are:

    1. server.ts

    2. browser.ts

    3. Handlers: You can define a common handlers.ts file or create a folder called handlers and define individual files

    4. Mocks: you can define a common mocks.ts file or create a folder called mocks and define individual files

    5. We will also create a parent folder called mockWorker where we will keep all the above files

  2. Inside the server.ts file, copy and paste the following code:

import { setupServer } from 'msw/node';
import handlers from './handlers';

export const server = setupServer(...handlers);
  1. Inside the brower.ts file, copy and paste the following code.
import { setupWorker } from 'msw/browser';
import handlers from './handlers';

export const worker = setupWorker(...handlers);
  1. Now create a custom hook called useMSWMockServer inside a hooks.ts file in the mockWorker folder we had created and paste the following code
import { useEffect, useState } from 'react';

function useMSWMockServer() {
  const [shouldRender, setShouldRender] = useState(
    process.env.NEXT_PUBLIC_ENABLE_API_MOCKING !== 'true'
  );

  useEffect(() => {
    if (process.env.NEXT_PUBLIC_ENABLE_API_MOCKING === 'true') {
      if (typeof window !== 'undefined') {
        import('./browser')
          .then(async ({ worker }) => {
            await worker.start();
            setShouldRender(true);
            return null;
          })
          .catch((err) => {
            console.error(err);
          });
      }
    }
  }, []);

  return shouldRender;
}

export default useMSWMockServer;

This will conditionally intialize the mock worker based on your development environment. Ideally you would have the env set to true while building the application locally but on staging or production you would have it set to false

  1. Create an index.ts file inside the handlers folder. We will import all the individual handlers in this index file and then export the file to be used in server.ts and browser.ts

  2. Now it's time to define the handlers. Let's create a user.handlers.ts file inside our handlers folder. Once the file is created we need to define our API mocking logic. Copy and paste the following code snippets in the user handler file

// users.handlers.ts
import { http, HttpResponse } from 'msw';
import {mockUser} from '../mocks/user.mocks.ts'

const API_URL = process.env.NEXT_PUBLIC_API_URL

const userHandlers = [
  http.get('/user', () =>
    HttpResponse.json({
      data: {
        users: Array.from({ length: 10 }, mockUser),
      },
    })
  ),
  http.get('/user/:id', () => HttpResponse.json({ data: { user: mockUser() } })),
];

export default userHandlers;
  1. Now we need to define a user.mocks.ts file inside our mocks folder. The mock files will have a function that will help us generate dummy data which can then be used inside the handler functions like the generateUser we used in the userHandlers function.
import { faker } from '@faker-js/faker';

export const mockUser = () => {
const user: IUser = {
    email: faker.internet.email(),
    firstName: faker.person.firstName(),
    lastName: faker.person.lastName(),
  }
  return mockUser;
}
  1. Now finally import all the handler files inside the index.ts file we created inside our handlers folder earlier
import userHandlers from './user.handlers.ts'

const handlers = [...userHandlers]

export default handlers

Yayy!!!! We now have all the files and code inside them setup. Let's start mocking!

  1. First inside you _app.ts file copy and paste the following code snippet.
import useMSWMockServer from '../apis/mocks/hook';

 // TODO: Remove this hook when the mock server is no longer needed.
  const shouldRender = useMSWMockServer();
  if (shouldRender === false) {
    return null;
  }
  1. Define you fetch calls and make sure they are hitting the same API url as defined in your handlers.

Setting up MSW in React.

  1. Install MSW in your project and set up the mockServiceWorker.js file in your app's public directory by following the first two steps mentioned in the above section.

  2. Once the mockServiceWorker.js file has been created, create a mockWorker folder similar to the one we have created in the above section.

  3. All further steps remain the same. The only difference is that in case of React is that server.ts is not required as well as the custom useMSWMockServer hook. Also, make sure to import the envs as they are done in React.

  4. To finally enable mocking copy and paste the following code in your main.ts file, assuming you have created your React app using Vite.

async function enableMocking() {
  if (import.meta.env.VITE_ENABLE_API_MOCKING === 'true') {
    const { worker } = await import('./apis/mocks/browser');
    worker.start();
  }
}

// TODO: Remove this once API is ready, remove before deploying to production
enableMocking()
  .then(() => {
    ReactDOM.createRoot(document.getElementById('root')!).render(
      <React.StrictMode>
        <App />
      </React.StrictMode>
    );
  })
  .catch((error) => {
    console.error(error);
  });

Happy mocking!!

ย