Skip to main content

Best API Mocking Libraries for JavaScript Testing in 2026

·PkgPulse Team

TL;DR

MSW (Mock Service Worker) is the 2026 standard for API mocking. MSW (~5M weekly downloads) intercepts requests at the network level using Service Workers (browser) or Node.js interceptors — your code never knows it's mocked. nock (~8M downloads) is Node.js-only and intercepts http/https module calls. Mirage.js (~400K) is browser-focused with a fake database layer. For most projects, MSW handles both browser and Node.js test environments seamlessly.

Key Takeaways

  • MSW: ~5M weekly downloads — browser + Node.js, network-level interception, no code changes
  • nock: ~8M downloads — Node.js only, intercepts http.request, widely used in legacy code
  • Mirage: ~400K downloads — browser-only, in-memory DB, REST + GraphQL
  • MSW v2 — native fetch interception, TypeScript-first, no polyfills needed
  • @mswjs/data — MSW companion for typed fake database

MSW (Mock Service Worker)

// MSW — define handlers once, use in browser + tests
// src/mocks/handlers.ts
import { http, HttpResponse } from 'msw';

export const handlers = [
  // GET handler
  http.get('/api/users', () => {
    return HttpResponse.json([
      { id: 1, name: 'Alice', email: 'alice@example.com' },
      { id: 2, name: 'Bob', email: 'bob@example.com' },
    ]);
  }),

  // GET with path params
  http.get('/api/users/:id', ({ params }) => {
    const { id } = params;
    return HttpResponse.json({
      id: Number(id),
      name: 'Alice',
      email: 'alice@example.com',
    });
  }),

  // POST handler
  http.post('/api/users', async ({ request }) => {
    const body = await request.json();
    return HttpResponse.json(
      { id: Date.now(), ...body },
      { status: 201 }
    );
  }),

  // Error simulation
  http.delete('/api/users/:id', () => {
    return HttpResponse.json(
      { message: 'Unauthorized' },
      { status: 401 }
    );
  }),
];
// MSW — browser setup (public/mockServiceWorker.js generated by MSW)
// src/mocks/browser.ts
import { setupWorker } from 'msw/browser';
import { handlers } from './handlers';

export const worker = setupWorker(...handlers);

// main.tsx — start worker in development
if (process.env.NODE_ENV === 'development') {
  const { worker } = await import('./mocks/browser');
  await worker.start({ onUnhandledRequest: 'bypass' });
}
// MSW — Node.js test setup (Vitest/Jest)
// src/mocks/server.ts
import { setupServer } from 'msw/node';
import { handlers } from './handlers';

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

// vitest.setup.ts
import { server } from './src/mocks/server';

beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
// MSW — override handlers in individual tests
import { server } from '../mocks/server';
import { http, HttpResponse } from 'msw';

describe('UserProfile', () => {
  it('shows loading state', async () => {
    server.use(
      http.get('/api/users/:id', async () => {
        await delay(500); // Simulate slow network
        return HttpResponse.json({ id: 1, name: 'Alice' });
      })
    );

    render(<UserProfile userId={1} />);
    expect(screen.getByText('Loading...')).toBeInTheDocument();
  });

  it('shows error state', async () => {
    server.use(
      http.get('/api/users/:id', () => {
        return HttpResponse.json(
          { message: 'Not found' },
          { status: 404 }
        );
      })
    );

    render(<UserProfile userId={999} />);
    await waitFor(() => {
      expect(screen.getByText('User not found')).toBeInTheDocument();
    });
  });
});

nock (Node.js)

// nock — intercepts Node.js http.request
import nock from 'nock';
import axios from 'axios';

describe('UserService', () => {
  afterEach(() => nock.cleanAll());

  it('fetches a user', async () => {
    // Setup mock
    nock('https://api.example.com')
      .get('/users/1')
      .reply(200, { id: 1, name: 'Alice' });

    // Call function under test (which uses axios internally)
    const user = await getUser(1);

    expect(user.name).toBe('Alice');
    // nock verifies the request was made
  });

  it('handles network errors', async () => {
    nock('https://api.example.com')
      .get('/users/1')
      .replyWithError('Network failure');

    await expect(getUser(1)).rejects.toThrow('Network failure');
  });

  it('handles delayed responses', async () => {
    nock('https://api.example.com')
      .get('/users')
      .delay(200)     // 200ms delay
      .reply(200, []);

    const users = await listUsers();
    expect(users).toEqual([]);
  });
});
// nock — with query params and headers
nock('https://api.example.com')
  .get('/users')
  .query({ page: '1', limit: '10' })              // Match query params
  .matchHeader('Authorization', /^Bearer .+/)      // Match header
  .reply(200, { users: [], total: 0 });

// POST with body matching
nock('https://api.example.com')
  .post('/users', { name: 'Alice', email: /alice@/ })  // Body matcher
  .reply(201, { id: 123 });

MSW + @mswjs/data (Typed Mock DB)

// @mswjs/data — in-memory typed database for MSW
import { factory, primaryKey, nullable } from '@mswjs/data';

// Define your data model
const db = factory({
  user: {
    id: primaryKey(String),
    name: String,
    email: String,
    role: String,
    createdAt: nullable(Date),
  },
  post: {
    id: primaryKey(String),
    title: String,
    body: String,
    authorId: String,
  },
});

// Seed data
db.user.create({ id: '1', name: 'Alice', email: 'alice@example.com', role: 'admin' });
db.user.create({ id: '2', name: 'Bob', email: 'bob@example.com', role: 'user' });

// Generate handlers from the db
export const handlers = [
  ...db.user.toHandlers('rest', 'https://api.example.com'),
  // Auto-generates: GET /users, GET /users/:id, POST /users, PATCH /users/:id, DELETE /users/:id
];

Comparison Table

LibraryBrowserNode.jsGraphQLTypeScriptNetwork Level
MSW✅ (ServiceWorker/interceptors)
nock✅ (http module)
Mirage✅ (pretender)
fetch-mockfetch only

When to Choose

ScenarioPick
New project, browser + Node.js testsMSW
Want to use mocks in browser during devMSW
Legacy Node.js project (fetch not used)nock
Backend microservice testingnock
Complex mock database with relationsMSW + @mswjs/data
Frontend-only, complex server stateMirage.js

Compare API mocking library package health on PkgPulse.

See the live comparison

View msw vs. nock on PkgPulse →

Comments

Stay Updated

Get the latest package insights, npm trends, and tooling tips delivered to your inbox.