DEV Community

Alex Spinov
Alex Spinov

Posted on

MSW Has a Free API Mocking Library: Mock REST and GraphQL APIs in Browser and Node.js Without Changing App Code

Your frontend depends on 5 APIs. Backend isn't ready. You create mockData.json, wrap fetch calls in if (process.env.NODE_ENV === 'development'), and your production code becomes littered with mock logic.

What if you could intercept network requests at the service worker level — without touching your application code?

That's MSW (Mock Service Worker). Your app makes real fetch calls. MSW intercepts them and returns mock responses. Your code doesn't know the difference.

Quick Start

npm install msw --save-dev
npx msw init ./public  # For browser usage
Enter fullscreen mode Exit fullscreen mode

Define Handlers

// mocks/handlers.ts
import { http, HttpResponse } from "msw";

export const handlers = [
  http.get("/api/users", () => {
    return HttpResponse.json([
      { id: "1", name: "Aleksej", email: "dev@example.com" },
      { id: "2", name: "Maria", email: "maria@example.com" },
    ]);
  }),

  http.post("/api/users", async ({ request }) => {
    const body = await request.json();
    return HttpResponse.json(
      { id: "3", ...body },
      { status: 201 }
    );
  }),

  http.get("/api/users/:id", ({ params }) => {
    const { id } = params;
    if (id === "999") {
      return HttpResponse.json(
        { error: "User not found" },
        { status: 404 }
      );
    }
    return HttpResponse.json({ id, name: "Aleksej" });
  }),

  http.delete("/api/users/:id", () => {
    return new HttpResponse(null, { status: 204 });
  }),
];
Enter fullscreen mode Exit fullscreen mode

Browser Setup (Development)

// mocks/browser.ts
import { setupWorker } from "msw/browser";
import { handlers } from "./handlers";

export const worker = setupWorker(...handlers);
Enter fullscreen mode Exit fullscreen mode
// main.tsx
async function enableMocking() {
  if (process.env.NODE_ENV !== "development") return;
  const { worker } = await import("./mocks/browser");
  return worker.start();
}

enableMocking().then(() => {
  ReactDOM.createRoot(document.getElementById("root")!).render(<App />);
});
Enter fullscreen mode Exit fullscreen mode

Your app's fetch calls work normally. MSW intercepts them at the network level. Open DevTools Network tab — you'll see the requests as if hitting a real API.

Node.js Setup (Testing)

// mocks/server.ts
import { setupServer } from "msw/node";
import { handlers } from "./handlers";

export const server = setupServer(...handlers);

// vitest.setup.ts
import { beforeAll, afterEach, afterAll } from "vitest";
import { server } from "./mocks/server";

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
Enter fullscreen mode Exit fullscreen mode
// tests/users.test.ts
import { http, HttpResponse } from "msw";
import { server } from "../mocks/server";

test("handles server error", async () => {
  // Override handler for this test only
  server.use(
    http.get("/api/users", () => {
      return HttpResponse.json({ error: "Server Error" }, { status: 500 });
    })
  );

  render(<UserList />);
  await expect(screen.findByText("Failed to load users")).resolves.toBeInTheDocument();
});
Enter fullscreen mode Exit fullscreen mode

GraphQL Mocking

import { graphql, HttpResponse } from "msw";

export const graphqlHandlers = [
  graphql.query("GetUser", ({ variables }) => {
    return HttpResponse.json({
      data: {
        user: { id: variables.id, name: "Aleksej", email: "dev@test.com" },
      },
    });
  }),

  graphql.mutation("CreateUser", ({ variables }) => {
    return HttpResponse.json({
      data: {
        createUser: { id: "new-id", ...variables.input },
      },
    });
  }),
];
Enter fullscreen mode Exit fullscreen mode

MSW vs Other Mocking

Feature MSW nock json-server Mirage
Browser support Yes No Separate process Yes
Node.js support Yes Yes Separate process No
Network-level Service Worker HTTP interceptor Real server Pretender
Code changes needed None None None Route setup
GraphQL support Yes Manual No No

When to Choose MSW

Choose MSW when:

  • Frontend development while APIs aren't ready
  • Testing components that make API calls
  • You want consistent mocks across browser and Node.js
  • You need to test error states, loading states, edge cases

Skip MSW when:

  • You only need simple test data (just use fixtures)
  • Backend is always available and stable
  • You're testing backend code (use real DB or test containers)

Start here: mswjs.io


Need custom data extraction, scraping, or automation? I build tools that collect and process data at scale — 78 actors on Apify Store and 265+ open-source repos. Email me: Spinov001@gmail.com | My Apify Actors

Top comments (0)