2025-12-25 12:37:10 +00:00
|
|
|
# MSW (Mock Service Worker) Setup Guide
|
|
|
|
|
|
|
|
|
|
## FE-API-019: API Mocking for Development
|
|
|
|
|
|
|
|
|
|
This guide explains how to use MSW (Mock Service Worker) for API mocking during development and testing.
|
|
|
|
|
|
|
|
|
|
## Overview
|
|
|
|
|
|
|
|
|
|
MSW allows you to intercept and mock HTTP requests at the network level, making it perfect for:
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2025-12-25 12:37:10 +00:00
|
|
|
- Development without a running backend
|
|
|
|
|
- Testing with consistent mock data
|
|
|
|
|
- Simulating error scenarios
|
|
|
|
|
- Testing offline behavior
|
|
|
|
|
|
|
|
|
|
## Configuration
|
|
|
|
|
|
|
|
|
|
### Environment Variable
|
|
|
|
|
|
|
|
|
|
MSW is controlled by the `VITE_USE_MSW` environment variable:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Enable MSW
|
|
|
|
|
VITE_USE_MSW=1 npm run dev
|
|
|
|
|
|
|
|
|
|
# Or use the dedicated script
|
|
|
|
|
npm run dev:mocks
|
|
|
|
|
|
|
|
|
|
# Disable MSW (default)
|
|
|
|
|
VITE_USE_MSW=0 npm run dev
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Initialization
|
|
|
|
|
|
|
|
|
|
MSW is automatically initialized in `main.tsx` when:
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2025-12-25 12:37:10 +00:00
|
|
|
- `VITE_USE_MSW` is set to `1` or `true`
|
|
|
|
|
- Running in development mode (`import.meta.env.DEV`)
|
|
|
|
|
|
|
|
|
|
The worker is started before the React app renders to ensure all requests are intercepted.
|
|
|
|
|
|
|
|
|
|
## File Structure
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
apps/web/src/mocks/
|
|
|
|
|
├── handlers.ts # Mock request handlers
|
|
|
|
|
├── browser.ts # Browser worker setup
|
|
|
|
|
├── node.ts # Node.js server setup (for tests)
|
|
|
|
|
├── index.ts # Exports
|
|
|
|
|
└── test-setup.ts # Test configuration
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Adding New Handlers
|
|
|
|
|
|
|
|
|
|
Edit `apps/web/src/mocks/handlers.ts` to add new mock handlers:
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
import { http, HttpResponse } from 'msw';
|
|
|
|
|
|
|
|
|
|
export const handlers = [
|
|
|
|
|
// GET request
|
|
|
|
|
http.get('/api/v1/endpoint', () => {
|
|
|
|
|
return HttpResponse.json({
|
2026-01-13 18:47:57 +00:00
|
|
|
data: {
|
|
|
|
|
/* mock data */
|
|
|
|
|
},
|
2025-12-25 12:37:10 +00:00
|
|
|
});
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
|
|
// POST request with body
|
|
|
|
|
http.post('/api/v1/endpoint', async ({ request }) => {
|
|
|
|
|
const body = await request.json();
|
2026-01-13 18:47:57 +00:00
|
|
|
return HttpResponse.json(
|
|
|
|
|
{
|
|
|
|
|
data: {
|
|
|
|
|
/* response */
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{ status: 201 },
|
|
|
|
|
);
|
2025-12-25 12:37:10 +00:00
|
|
|
}),
|
|
|
|
|
|
|
|
|
|
// Request with parameters
|
|
|
|
|
http.get('/api/v1/endpoint/:id', ({ params }) => {
|
|
|
|
|
const { id } = params;
|
|
|
|
|
return HttpResponse.json({
|
2026-01-13 18:47:57 +00:00
|
|
|
data: { id /* ... */ },
|
2025-12-25 12:37:10 +00:00
|
|
|
});
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
|
|
// Request with query parameters
|
|
|
|
|
http.get('/api/v1/endpoint', ({ request }) => {
|
|
|
|
|
const url = new URL(request.url);
|
|
|
|
|
const param = url.searchParams.get('param');
|
|
|
|
|
return HttpResponse.json({
|
2026-01-13 18:47:57 +00:00
|
|
|
data: { param /* ... */ },
|
2025-12-25 12:37:10 +00:00
|
|
|
});
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
|
|
// Error response
|
|
|
|
|
http.get('/api/v1/error', () => {
|
|
|
|
|
return HttpResponse.json(
|
|
|
|
|
{ error: 'Something went wrong' },
|
2026-01-13 18:47:57 +00:00
|
|
|
{ status: 500 },
|
2025-12-25 12:37:10 +00:00
|
|
|
);
|
|
|
|
|
}),
|
|
|
|
|
];
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## URL Matching
|
|
|
|
|
|
|
|
|
|
**Important**: MSW handlers must match the **full URL path** that the client sends.
|
|
|
|
|
|
|
|
|
|
Since `apiClient` uses `baseURL: env.API_URL` (e.g., `http://127.0.0.1:8080/api/v1`), handlers should match:
|
|
|
|
|
|
|
|
|
|
- ✅ `/api/v1/roles` (matches full path)
|
|
|
|
|
- ✅ `http://127.0.0.1:8080/api/v1/roles` (matches full URL)
|
|
|
|
|
- ❌ `/roles` (won't match if baseURL is set)
|
|
|
|
|
|
|
|
|
|
However, MSW can match relative paths if configured correctly. The current setup uses relative paths like `/api/v1/*`.
|
|
|
|
|
|
|
|
|
|
## Current Handlers
|
|
|
|
|
|
|
|
|
|
The following endpoints are currently mocked:
|
|
|
|
|
|
|
|
|
|
### Authentication
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2025-12-25 12:37:10 +00:00
|
|
|
- `POST /api/auth/login`
|
|
|
|
|
- `POST /api/auth/register`
|
|
|
|
|
- `POST /api/auth/refresh`
|
|
|
|
|
- `GET /api/users/profile`
|
|
|
|
|
- `PUT /api/users/profile`
|
|
|
|
|
|
|
|
|
|
### Chat
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2025-12-25 12:37:10 +00:00
|
|
|
- `GET /api/chat/conversations`
|
|
|
|
|
- `GET /api/chat/conversations/:id/messages`
|
|
|
|
|
- `POST /api/chat/conversations/:id/messages`
|
|
|
|
|
|
|
|
|
|
### Library
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2025-12-25 12:37:10 +00:00
|
|
|
- `GET /api/library/tracks`
|
|
|
|
|
- `POST /api/library/tracks`
|
|
|
|
|
|
|
|
|
|
### Health & Errors
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2025-12-25 12:37:10 +00:00
|
|
|
- `GET /api/health`
|
|
|
|
|
- `GET /api/error/500`
|
|
|
|
|
- `GET /api/error/404`
|
|
|
|
|
- `GET /api/error/timeout`
|
|
|
|
|
|
|
|
|
|
## Testing with MSW
|
|
|
|
|
|
|
|
|
|
### In Unit Tests
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
import { server } from '@/mocks/test-setup';
|
|
|
|
|
import { handlers, errorHandlers } from '@/mocks/handlers';
|
|
|
|
|
|
|
|
|
|
beforeAll(() => {
|
|
|
|
|
server.listen();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
|
server.resetHandlers();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
afterAll(() => {
|
|
|
|
|
server.close();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Use error handlers for error scenarios
|
|
|
|
|
test('handles error', () => {
|
|
|
|
|
server.use(...errorHandlers);
|
|
|
|
|
// Test error handling
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### In E2E Tests
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Run E2E tests with MSW
|
|
|
|
|
npm run test:e2e:msw
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Best Practices
|
|
|
|
|
|
|
|
|
|
1. **Keep handlers realistic**: Mock data should match the real API structure
|
|
|
|
|
2. **Use variables**: Store mock data in constants for reusability
|
|
|
|
|
3. **Handle edge cases**: Add handlers for error scenarios (400, 401, 500, etc.)
|
|
|
|
|
4. **Document changes**: Update this guide when adding new handlers
|
|
|
|
|
5. **Test both paths**: Test with MSW enabled and disabled
|
|
|
|
|
|
|
|
|
|
## Troubleshooting
|
|
|
|
|
|
|
|
|
|
### MSW not intercepting requests
|
|
|
|
|
|
|
|
|
|
1. Check that `VITE_USE_MSW=1` is set
|
|
|
|
|
2. Verify `mockServiceWorker.js` exists in `public/`
|
|
|
|
|
3. Check browser console for MSW initialization messages
|
|
|
|
|
4. Ensure handlers match the exact URL path
|
|
|
|
|
|
|
|
|
|
### Handler not matching
|
|
|
|
|
|
|
|
|
|
1. Check the exact URL being sent (check Network tab)
|
|
|
|
|
2. Verify the handler path matches exactly
|
|
|
|
|
3. Use `onUnhandledRequest: 'warn'` to see unmatched requests
|
|
|
|
|
|
|
|
|
|
### Service Worker issues
|
|
|
|
|
|
|
|
|
|
1. Clear browser cache and service workers
|
|
|
|
|
2. Unregister old service workers in DevTools > Application > Service Workers
|
|
|
|
|
3. Restart the dev server
|
|
|
|
|
|
|
|
|
|
## Updating Mock Service Worker
|
|
|
|
|
|
|
|
|
|
If MSW version is updated, regenerate the service worker:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
npx msw init public/ --save
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Related Documentation
|
|
|
|
|
|
|
|
|
|
- [MSW Documentation](https://mswjs.io/)
|
|
|
|
|
- [MSW GitHub](https://github.com/mswjs/msw)
|