Mocking API calls using MSW in Next.js and React
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
- 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
- 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
Once you run the above command you can see the
mockServiceWorker.js
file created in yourpublic
directory. Now we need to define a few more files, which are:server.ts
browser.ts
Handlers: You can define a common
handlers.ts
file or create a folder calledhandlers
and define individual filesMocks: you can define a common
mocks.ts
file or create a folder calledmocks
and define individual filesWe will also create a parent folder called
mockWorker
where we will keep all the above files
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);
- 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);
- Now create a custom hook called
useMSWMockServer
inside ahooks.ts
file in themockWorker
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
Create an
index.ts
file inside thehandlers
folder. We will import all the individual handlers in this index file and then export the file to be used inserver.ts
andbrowser.ts
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;
- 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 thegenerateUser
we used in theuserHandlers
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;
}
- 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!
- 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;
}
- Define you fetch calls and make sure they are hitting the same API url as defined in your handlers.
Setting up MSW in React.
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.Once the
mockServiceWorker.js
file has been created, create amockWorker
folder similar to the one we have created in the above section.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 customuseMSWMockServer
hook. Also, make sure to import theenvs
as they are done in React.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!!