How to Configure MSW for Next.js Apps
Configuring Mock Service Worker (MSW) within a Next.js architecture demands precise alignment between client-side hydration, server-side rendering, and the browser service worker lifecycle. This guide delivers a deterministic configuration pattern for intercepting network requests during local development simulation, ensuring consistent API mocking across SSR, SSG, and CSR execution contexts. For foundational architecture principles and framework-agnostic routing strategies, refer to the broader Tool-Specific Implementation & Setup documentation.
1. Environment Initialization & Worker Placement
Execution:
- Install the core package as a development dependency:
npm install msw --save-dev
- Generate the service worker script in the Next.js
publicdirectory:
npx msw init public --save
- Verify static asset availability by navigating to
http://localhost:3000/mockServiceWorker.jsin your browser.
Prevention Strategy:
Platform teams must commit mockServiceWorker.js directly to version control. Next.js serves this file as a static asset, and omitting it from the repository causes intermittent registration failures across developer machines and CI pipelines. Avoid dynamic generation during build steps to guarantee deterministic worker versions across staging and production-like environments. If using a monorepo, ensure the worker script resides in the consuming Next.js app’s public directory rather than a shared package root.
2. Defining Type-Safe Route Handlers
Execution:
Centralize interception logic in src/mocks/handlers.ts. Import http and HttpResponse from msw to construct explicit request matchers. Align resolver payloads with your OpenAPI or GraphQL schemas to prevent runtime type mismatches during frontend development.
import { http, HttpResponse } from 'msw';
export const handlers = [
http.get('/api/v1/users', ({ request }) => {
const url = new URL(request.url);
const role = url.searchParams.get('role');
return HttpResponse.json({
id: 'usr_123',
role: role || 'viewer',
status: 'active'
}, { status: 200 });
})
];
Prevention Strategy:
Enforce strict HTTP method matching to prevent accidental interception of POST or PATCH requests intended for live endpoints. When implementing dynamic path parameters or complex query string matching, consult the Mock Service Worker (MSW) Setup reference for advanced routing strategies. API architects should mandate TypeScript interfaces for all mock payloads, ensuring that contract drift is caught at compile time rather than during QA validation.
3. Conditional Worker Initialization (CSR vs SSR)
Execution:
Next.js executes code in both Node.js and browser environments. To prevent service worker registration failures during server-side rendering, implement a conditional bootstrap in src/mocks/browser.ts:
import { setupWorker } from 'msw/browser';
import { handlers } from './handlers';
export const worker = typeof window !== 'undefined'
? setupWorker(...handlers)
: undefined;
Import and invoke worker?.start() exclusively within a useEffect hook or a client-side entry point. For intercepting Next.js API routes (app/api/*), instantiate setupServer from msw/node and bind it to a custom test server or local proxy layer.
Prevention Strategy:
Never invoke setupWorker in server components or middleware. Unconditional initialization triggers ReferenceError: window is not defined during SSR, crashing the hydration process. Full-stack developers should isolate mock initialization behind a feature flag or environment variable (NEXT_PUBLIC_ENABLE_MSW=true) to prevent accidental activation in production builds. When using the App Router, ensure fetch caching is disabled during mock sessions by appending { cache: 'no-store' } to prevent Next.js from returning stale, pre-rendered responses.
4. Next.js Configuration & Middleware Integration
Execution:
Adjust next.config.js to prevent framework-level rewrites from bypassing the service worker during development. Disable aggressive /api/* path redirections in the rewrites array when process.env.NODE_ENV === 'development'.
If utilizing Next.js Middleware, configure it to pass through requests originating from localhost when the x-msw-bypass header is present. This ensures the service worker retains control over network interception without conflicting with edge routing logic.
Prevention Strategy:
QA engineers must integrate worker initialization into Playwright or Cypress test runners by calling worker.start({ onUnhandledRequest: 'warn' }) before test suites execute. This guarantees isolated mock states per test run and prevents cross-test pollution. Platform teams should enforce a strict onUnhandledRequest: 'error' policy in CI environments to catch missing handlers before they reach staging. Implement a custom next.config.js environment check that completely strips mock dependencies from the production bundle using webpack.IgnorePlugin.
5. Validation & Local Development Simulation Workflow
Execution:
Launch the application using npm run dev. Open browser DevTools, navigate to the Network tab, and filter by mockServiceWorker.js. Verify that intercepted requests display (mock) in the initiator column.
Create a centralized bootstrapper in mocks/index.ts to toggle between development and production modes:
export async function enableMocking() {
if (process.env.NODE_ENV !== 'development') return;
const { worker } = await import('./browser');
return worker.start({
onUnhandledRequest: 'warn',
});
}
Prevention Strategy:
API architects should validate that unhandled requests fallback gracefully to the live backend or return a 404 with clear diagnostic headers. Implement strict environment checks to ensure mocks never leak into deployed environments. QA teams should automate network assertion checks in E2E pipelines, verifying that every mocked endpoint returns the expected schema and status code. Regularly audit handler coverage against your API specification to prevent untested edge cases from reaching production.
Conclusion
Properly configuring MSW in Next.js eliminates flaky frontend tests, accelerates local iteration cycles, and decouples UI development from backend availability. By enforcing strict handler typing, conditional worker initialization, and environment-aware routing, development teams achieve reproducible local API gateway simulation without compromising SSR performance or deployment security. Adhering to these deterministic patterns ensures that mock service workers operate as reliable, isolated network proxies throughout the entire development lifecycle.