Mocking APIs with MSW
In modern frontend development, building and testing React applications often requires reliable API responses — even before the backend is ready. Instead of relying on unstable endpoints or waiting for real data, developers can simulate server behavior directly in the browser using Mock Service Worker (MSW).
MSW is a powerful tool that intercepts HTTP requests made by your app and serves mocked responses, all while using the browser’s native Service Worker API. Unlike traditional mocking tools that rely on hardcoded stubs or test-only environments, MSW works at the network level. This means your application behaves as if it’s communicating with a real server, enabling realistic development, testing, and debugging — with zero backend dependency.
Whether you’re building features locally, running integration tests, or simulating failure scenarios, MSW empowers you to control API behavior with precision and clarity — making it an essential tool in the modern React developer’s toolkit.
📚 Learn more: Mock Service Worker – Quick Start Guide
[!TIP] Verify MSW is Running: When your app starts, look for the
[MSW] Mocking enabled.message in your browser’s console. If you don’t see it, your worker might not be registered correctly.
Getting Hands-On with MSW
To demonstrate MSW in action, let’s walk through a hands-on example where we simulate a simple API for fetching user information in a React app. Instead of relying on a real backend, we’ll use MSW to intercept the /api/user request and return a mocked JSON response directly in the browser.
This approach not only allows us to start building our frontend independently, but also makes it easier to test edge cases, simulate failures, and ensure consistent development workflows.
The steps below will show you how to: - Define mock API handlers using MSW - Register the service worker for local development - Use fetch() inside a React component and receive a mocked response
Let’s start by creating the request handler for /api/user.
Step 1: Install MSW
npm install msw@latest📁 Folder Structure (recommended)
src/
mocks/
handlers.ts
browser.ts
index.tsxDefines a list of mock API endpoints and how to respond to them.
📄handlers.ts :
// src/mocks/handlers.ts
import { http, HttpResponse } from 'msw'
export const handlers = [
http.get('/api/user', () => {
return HttpResponse.json({ id: 1, name: 'Amit Verma' });
}),
];📌 Sets up the MSW worker in the browser and uses the handlers from 📄handlers.ts.
// src/mocks/browser.ts
import { setupWorker } from 'msw/browser'
import { handlers } from './handlers'
export const worker = setupWorker(...handlers);
worker.start({
onUnhandledRequest: 'warn'
});setupWorker() starts a Service Worker that listens to fetch and intercepts API calls in the browser. 🧠 This acts like your mock backend — it returns fake data instead of calling the real server.
Then in 📄index.tsx:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const startApp = () => {
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
};
if (process.env.NODE_ENV === 'development') {
import('./mocks/browser').then(({ worker }) => {
worker.start({ onUnhandledRequest: 'warn' }).then(() => {
startApp(); // ⏳ Only render app after MSW is ready
});
});
} else {
startApp();
}
reportWebVitals();📄App.tsx
import React from 'react';
import './App.css';
import MyComp from './MyComp';
function App() {
return (
<div className="App">
<MyComp />
</div>
);
}
export default App;📄MyComp.tsx :
import React, { useEffect, useState } from 'react';
type User = {
id: number;
name: string;
};
const MyComp = () => {
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
fetch('/api/user')
.then(async (response) => {
const contentType = response.headers.get('Content-Type');
if (contentType && contentType.includes('application/json')) {
const data = await response.json();
setUser(data);
} else {
throw new Error('Response is not JSON');
}
})
.catch(error => console.error('Error fetching user:', error));
}, []);
return (
<div>
{user ? (
<div>
<h1>User Details</h1>
<p>ID: {user.id}</p>
<p>Name: {user.name}</p>
<p>Role: {user.role}</p>
</div>
) : (
<p>Loading user...</p>
)}
</div>
);
};
export default MyComp;we also need to run
npx msw init public/ --saveThis command copies the actual service worker file (mockServiceWorker.js) into your public/ folder. Why It’s Critical • This file is registered by your browser via setupWorker(…) • Without it, you’ll see errors like:
[MSW] Failed to register a Service Worker
[MSW] Error: TypeError: Failed to register a ServiceWorker for scope...
🔄 Flow Summary
App → fetch('/api/user')
↓
[MSW Service Worker]
↓
handlers.ts → Matches rest.get('/api/user')
↓
Returns mocked response (e.g. { id: 1, name: "Amit" })🧪 Test It
In your React component, write:
useEffect(() => {
fetch('/api/user')
.then(res => res.json())
.then(data => console.log('Mocked User:', data))
}, [])When you run your app, it should log: Mocked User: { id: 1, name: ‘Amit Verma’, role: ‘Lead Software Engineer’ }
📄package.json
{
"name": "my-react-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.126",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"msw": "^2.10.4",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-scripts": "5.0.1",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"msw": {
"workerDirectory": [
"public"
]
}
}