CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-msw

Seamless REST/GraphQL API mocking library for browser and Node.js.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

browser-setup.mddocs/

Browser Setup

Browser setup provides Service Worker-based request interception for browser environments with lifecycle control and runtime handler management.

Capabilities

Setup Worker Function

Creates a Service Worker-based request interceptor for browser environments.

/**
 * Sets up a Service Worker for intercepting requests in browser environments
 * @param handlers - Array of request handlers (HTTP, GraphQL, WebSocket)
 * @returns SetupWorker instance with lifecycle control methods
 */
function setupWorker(...handlers: Array<RequestHandler | WebSocketHandler>): SetupWorker;

interface SetupWorker {
  /** Start the Service Worker and begin intercepting requests */
  start(options?: StartOptions): Promise<ServiceWorkerRegistration | undefined>;
  /** Stop the Service Worker and cease request interception */
  stop(): void;
  /** Add runtime request handlers without restarting */
  use(...handlers: Array<RequestHandler | WebSocketHandler>): void;
  /** Reset handlers to initial set or provided handlers */
  resetHandlers(...handlers?: Array<RequestHandler | WebSocketHandler>): void;
  /** Restore handlers to their original state */
  restoreHandlers(): void;
  /** Get read-only list of currently active handlers */
  listHandlers(): ReadonlyArray<RequestHandler | WebSocketHandler>;
  /** Event emitter for lifecycle events */
  events: LifeCycleEventEmitter<LifeCycleEventsMap>;
}

Usage Examples:

import { http, HttpResponse, setupWorker } from "msw";

// Define request handlers
const handlers = [
  http.get('/api/user', () => {
    return HttpResponse.json({ name: 'John Doe' });
  }),
  
  http.post('/api/login', async ({ request }) => {
    const { username, password } = await request.json();
    return HttpResponse.json({ token: 'fake-token' });
  })
];

// Create worker instance
const worker = setupWorker(...handlers);

// Start the worker
async function startMocking() {
  try {
    await worker.start();
    console.log('MSW started successfully');
  } catch (error) {
    console.error('Failed to start MSW:', error);
  }
}

// Call in application bootstrap
startMocking();

Start Method

Start the Service Worker and begin request interception.

/**
 * Start the Service Worker and begin intercepting requests
 * @param options - Configuration options for worker startup
 * @returns Promise resolving to ServiceWorkerRegistration or undefined
 */
start(options?: StartOptions): Promise<ServiceWorkerRegistration | undefined>;

interface StartOptions {
  /** Service Worker configuration */
  serviceWorker?: {
    /** Custom URL for the Service Worker script */
    url?: string;
    /** Service Worker registration options */
    options?: RegistrationOptions;
  };
  /** Suppress startup logs and warnings */
  quiet?: boolean;
  /** How to handle requests not matched by any handler */
  onUnhandledRequest?: 'bypass' | 'warn' | 'error';
  /** Whether to wait for existing Service Worker to update */
  waitUntilReady?: boolean;
}

Usage Examples:

// Basic startup
await worker.start();

// Custom Service Worker location
await worker.start({
  serviceWorker: {
    url: '/custom-sw.js'
  }
});

// Quiet mode (suppress logs)
await worker.start({
  quiet: true
});

// Strict unhandled request handling
await worker.start({
  onUnhandledRequest: 'error'
});

// Full configuration
await worker.start({
  serviceWorker: {
    url: '/mockServiceWorker.js',
    options: {
      scope: '/api/'
    }
  },
  quiet: false,
  onUnhandledRequest: 'warn',
  waitUntilReady: true
});

// Handle startup errors
try {
  const registration = await worker.start();
  
  if (registration) {
    console.log('Service Worker registered:', registration.scope);
  }
} catch (error) {
  if (error.message.includes('Service Worker')) {
    console.warn('Service Worker not supported');
  } else {
    console.error('Failed to start MSW:', error);
  }
}

Stop Method

Stop the Service Worker and cease request interception.

/**
 * Stop the Service Worker and cease request interception
 * Unregisters the Service Worker and cleans up resources
 */
stop(): void;

Usage Examples:

// Stop mocking (e.g., in production builds)
worker.stop();

// Conditional stopping
if (process.env.NODE_ENV === 'production') {
  worker.stop();
}

// Clean shutdown in application lifecycle
window.addEventListener('beforeunload', () => {
  worker.stop();
});

Runtime Handler Management

Add, reset, and restore request handlers at runtime without restarting the worker.

/**
 * Add runtime request handlers without restarting the Service Worker
 * @param handlers - Additional handlers to add to current set
 */
use(...handlers: Array<RequestHandler | WebSocketHandler>): void;

/**
 * Reset handlers to initial set or provided handlers
 * @param handlers - Optional new set of handlers (defaults to initial handlers)
 */
resetHandlers(...handlers?: Array<RequestHandler | WebSocketHandler>): void;

/**
 * Restore handlers to their original unmodified state
 * Removes any runtime handlers added via use()
 */
restoreHandlers(): void;

/**
 * Get read-only list of currently active handlers
 * @returns Array of currently active request handlers
 */
listHandlers(): ReadonlyArray<RequestHandler | WebSocketHandler>;

Usage Examples:

import { http, HttpResponse } from "msw";

// Add handlers at runtime
worker.use(
  http.get('/api/new-endpoint', () => {
    return HttpResponse.json({ data: 'new data' });
  }),
  
  http.post('/api/temp-endpoint', () => {
    return HttpResponse.json({ success: true });
  })
);

// Reset to initial handlers
worker.resetHandlers();

// Reset to specific handlers
worker.resetHandlers(
  http.get('/api/reset', () => {
    return HttpResponse.json({ reset: true });
  })
);

// Restore to original state
worker.restoreHandlers();

// Check current handlers
const currentHandlers = worker.listHandlers();
console.log(`Currently ${currentHandlers.length} handlers active`);

// Dynamic handler management based on conditions
function updateHandlersForFeature(enabled: boolean) {
  if (enabled) {
    worker.use(
      http.get('/api/feature', () => {
        return HttpResponse.json({ feature: 'enabled' });
      })
    );
  } else {
    worker.resetHandlers(); // Remove feature handlers
  }
}

// Test-specific handler overrides
function setupTestHandlers() {
  worker.use(
    http.get('/api/test-data', () => {
      return HttpResponse.json({ test: true, data: mockData });
    })
  );
}

function teardownTestHandlers() {
  worker.restoreHandlers();
}

Lifecycle Events

Monitor and respond to MSW lifecycle events.

interface LifeCycleEventEmitter<EventsMap> {
  /** Add event listener for lifecycle events */
  on<EventType extends keyof EventsMap>(
    event: EventType,
    listener: (...args: EventsMap[EventType]) => void
  ): void;
  
  /** Remove specific event listener */
  removeListener<EventType extends keyof EventsMap>(
    event: EventType,
    listener: (...args: EventsMap[EventType]) => void
  ): void;
  
  /** Remove all listeners for event or all events */
  removeAllListeners(event?: keyof EventsMap): void;
}

interface LifeCycleEventsMap {
  /** Fired when a request starts being processed */
  'request:start': [args: { request: Request; requestId: string }];
  /** Fired when a request matches a handler */
  'request:match': [args: { request: Request; requestId: string }];
  /** Fired when a request doesn't match any handler */
  'request:unhandled': [args: { request: Request; requestId: string }];
  /** Fired when request processing ends */
  'request:end': [args: { request: Request; requestId: string }];
  /** Fired when a mocked response is sent */
  'response:mocked': [args: { response: Response; request: Request; requestId: string }];
  /** Fired when a request bypasses MSW */
  'response:bypass': [args: { response: Response; request: Request; requestId: string }];
  /** Fired when an unhandled exception occurs */
  'unhandledException': [args: { error: Error; request: Request; requestId: string }];
}

Usage Examples:

// Monitor all requests
worker.events.on('request:start', ({ request, requestId }) => {
  console.log('Request started:', request.method, request.url, requestId);
});

// Track matched requests
worker.events.on('request:match', ({ request, requestId }) => {
  console.log('Request matched handler:', request.url, requestId);
});

// Handle unmatched requests
worker.events.on('request:unhandled', ({ request, requestId }) => {
  console.warn('Unhandled request:', request.method, request.url, requestId);
  
  // Could trigger notifications or logging
  if (request.url.includes('/api/')) {
    console.error('API request not mocked!');
  }
});

// Track mocked responses
worker.events.on('response:mocked', ({ response, request, requestId }) => {
  console.log('Mocked response sent:', response.status, request.url, requestId);
});

// Monitor bypassed requests
worker.events.on('response:bypass', ({ response, request, requestId }) => {
  console.log('Request bypassed MSW:', request.url, requestId);
});

// Analytics integration
worker.events.on('request:match', ({ request, requestId }) => {
  analytics.track('msw_request_mocked', {
    method: request.method,
    url: request.url,
    requestId,
    timestamp: Date.now()
  });
});

// Development debugging
if (process.env.NODE_ENV === 'development') {
  worker.events.on('request:unhandled', ({ request, requestId }) => {
    // Show notification for unmocked API calls
    showDevNotification(`Unmocked API call: ${request.url} (${requestId})`);
  });
}

// Cleanup event listeners
function cleanup() {
  worker.events.removeAllListeners();
}

Service Worker File Setup

Initialize MSW Service Worker file in your application.

CLI Setup:

# Initialize MSW in your public directory
npx msw init public/ --save

# This copies mockServiceWorker.js to public/mockServiceWorker.js
# and optionally saves the path in package.json

Manual Setup:

// In your main application file (e.g., src/main.ts)
async function enableMocking() {
  if (process.env.NODE_ENV !== 'development') {
    return;
  }

  const { worker } = await import('./mocks/browser');
  
  // Start the worker
  return worker.start({
    onUnhandledRequest: 'bypass'
  });
}

enableMocking().then(() => {
  // Start your React/Vue/Angular app
  ReactDOM.render(<App />, document.getElementById('root'));
});

Create Mocks File:

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

export const worker = setupWorker(...handlers);
// src/mocks/handlers.ts
import { http, HttpResponse } from 'msw';

export const handlers = [
  http.get('/api/user', () => {
    return HttpResponse.json({
      id: 'c7b3d8e0-5e0b-4b0f-8b3a-3b9f4b3d3b3d',
      firstName: 'John',
      lastName: 'Maverick',
    });
  }),
];

Browser Environment Considerations

Handle browser-specific scenarios and limitations.

// Feature detection
async function startMockingIfSupported() {
  if (!('serviceWorker' in navigator)) {
    console.warn('Service Worker not supported');
    return;
  }
  
  try {
    await worker.start();
  } catch (error) {
    console.warn('Could not start Service Worker:', error);
  }
}

// HTTPS requirement handling
if (location.protocol === 'https:' || location.hostname === 'localhost') {
  worker.start({
    quiet: process.env.NODE_ENV === 'production'
  });
} else {
  console.warn('MSW requires HTTPS or localhost');
}

// Development vs production
if (process.env.NODE_ENV === 'development') {
  worker.start({
    onUnhandledRequest: 'warn'
  });
} else if (process.env.NODE_ENV === 'test') {
  worker.start({
    quiet: true,
    onUnhandledRequest: 'error'
  });
}

// Hot module replacement support
if (module.hot) {
  module.hot.accept('./handlers', () => {
    worker.resetHandlers();
  });
}

Types

// Setup types
interface SetupWorker {
  start(options?: StartOptions): Promise<ServiceWorkerRegistration | undefined>;
  stop(): void;
  use(...handlers: Array<RequestHandler | WebSocketHandler>): void;
  resetHandlers(...handlers?: Array<RequestHandler | WebSocketHandler>): void;
  restoreHandlers(): void;
  listHandlers(): ReadonlyArray<RequestHandler | WebSocketHandler>;
  events: LifeCycleEventEmitter<LifeCycleEventsMap>;
}

// Configuration types
interface StartOptions {
  serviceWorker?: {
    url?: string;
    options?: RegistrationOptions;
  };
  quiet?: boolean;
  onUnhandledRequest?: 'bypass' | 'warn' | 'error';
  waitUntilReady?: boolean;
}

// Service Worker types
interface RegistrationOptions {
  scope?: string;
  type?: WorkerType;
  updateViaCache?: ServiceWorkerUpdateViaCache;
}

// Handler types (re-exported from core)
type RequestHandler = HttpHandler | GraphQLHandler | WebSocketHandler;
type WebSocketHandler = import('../core/handlers/WebSocketHandler').WebSocketHandler;

docs

browser-setup.md

cli.md

graphql-handlers.md

http-handlers.md

index.md

nodejs-setup.md

react-native-setup.md

response-creation.md

utilities.md

websocket-handlers.md

tile.json