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

nodejs-setup.mddocs/

Node.js Setup

Node.js setup provides low-level request interception for Node.js environments using HTTP/HTTPS module patching, ideal for testing scenarios and server-side mocking.

Capabilities

Setup Server Function

Creates a low-level request interceptor for Node.js environments.

/**
 * Sets up request interception for Node.js environments
 * @param handlers - Array of request handlers (HTTP, GraphQL, WebSocket)
 * @returns SetupServer instance with lifecycle control methods
 */
function setupServer(...handlers: Array<RequestHandler | WebSocketHandler>): SetupServer;

interface SetupServer {
  /** Start intercepting requests */
  listen(options?: ListenOptions): void;
  /** Stop intercepting requests and restore original behavior */
  close(): 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, setupServer } from "msw/node";

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

// Create server instance
const server = setupServer(...handlers);

// Start interception (typically in test setup)
server.listen();

// Stop interception (typically in test teardown)
server.close();

Listen Method

Start intercepting requests with optional configuration.

/**
 * Start intercepting requests
 * @param options - Configuration options for request interception
 */
listen(options?: ListenOptions): void;

interface ListenOptions {
  /** How to handle requests not matched by any handler */
  onUnhandledRequest?: 'bypass' | 'warn' | 'error' | ((request: Request, print: { warning(): void; error(): void }) => void);
}

Usage Examples:

// Basic setup - start intercepting
server.listen();

// Warn about unhandled requests
server.listen({
  onUnhandledRequest: 'warn'
});

// Error on unhandled requests (strict mode)
server.listen({
  onUnhandledRequest: 'error'
});

// Custom unhandled request handling
server.listen({
  onUnhandledRequest(request, print) {
    // Custom logic for unhandled requests
    if (request.url.includes('/api/')) {
      print.error();
    } else {
      print.warning();
    }
  }
});

// Bypass unhandled requests silently (default)
server.listen({
  onUnhandledRequest: 'bypass'
});

Close Method

Stop intercepting requests and restore original HTTP behavior.

/**
 * Stop intercepting requests and restore original behavior
 * Restores Node.js built-in HTTP/HTTPS modules to their original state
 */
close(): void;

Usage Examples:

// Stop interception
server.close();

// In test lifecycle
afterAll(() => {
  server.close();
});

// Conditional cleanup
if (process.env.NODE_ENV === 'test') {
  process.on('exit', () => {
    server.close();
  });
}

Runtime Handler Management

Add, reset, and restore request handlers at runtime.

/**
 * Add runtime request handlers without restarting
 * @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:

// Add handlers for specific test
beforeEach(() => {
  server.use(
    http.get('https://api.example.com/test-data', () => {
      return HttpResponse.json({ test: true });
    })
  );
});

// Reset to original handlers after each test
afterEach(() => {
  server.resetHandlers();
});

// Override specific handler for one test
test('should handle error scenario', async () => {
  server.use(
    http.get('https://api.example.com/user', () => {
      return HttpResponse.json(
        { error: 'User not found' },
        { status: 404 }
      );
    })
  );
  
  // Test error handling...
});

// Restore to completely clean state
afterAll(() => {
  server.restoreHandlers();
});

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

Testing Integration

Common patterns for integrating MSW with testing frameworks.

Jest Integration:

// tests/setup.ts
import { beforeAll, afterEach, afterAll } from '@jest/globals';
import { setupServer } from 'msw/node';
import { handlers } from '../src/mocks/handlers';

const server = setupServer(...handlers);

// Start server before all tests
beforeAll(() => {
  server.listen({
    onUnhandledRequest: 'error'
  });
});

// Reset handlers after each test
afterEach(() => {
  server.resetHandlers();
});

// Clean up after all tests
afterAll(() => {
  server.close();
});

export { server };

Vitest Integration:

// vitest.setup.ts
import { beforeAll, afterEach, afterAll } from 'vitest';
import { setupServer } from 'msw/node';
import { handlers } from './src/mocks/handlers';

const server = setupServer(...handlers);

beforeAll(() => {
  server.listen({ onUnhandledRequest: 'error' });
});

afterEach(() => {
  server.resetHandlers();
});

afterAll(() => {
  server.close();
});

Test-Specific Handlers:

import { server } from './setup';
import { http, HttpResponse } from 'msw';

describe('User API', () => {
  test('handles server error', async () => {
    // Override handler for this test
    server.use(
      http.get('https://api.example.com/user', () => {
        return HttpResponse.json(
          { error: 'Internal server error' },
          { status: 500 }
        );
      })
    );

    // Your test code that expects the error...
  });

  test('handles network error', async () => {
    server.use(
      http.get('https://api.example.com/user', () => {
        return HttpResponse.error();
      })
    );

    // Your test code that expects network error...
  });
});

Environment-Specific Setup

Handle different Node.js environments and configurations.

// Development server setup
if (process.env.NODE_ENV === 'development') {
  const { setupServer } = require('msw/node');
  const { handlers } = require('./mocks/handlers');
  
  const server = setupServer(...handlers);
  server.listen({
    onUnhandledRequest: 'warn'
  });
  
  console.log('Mock server running');
}

// Test environment setup
if (process.env.NODE_ENV === 'test') {
  global.mockServer = setupServer();
  
  // Automatically clean up on process exit
  process.on('beforeExit', () => {
    global.mockServer.close();
  });
}

// CI environment setup
const server = setupServer(...handlers);

if (process.env.CI) {
  server.listen({
    onUnhandledRequest: 'error' // Strict mode in CI
  });
} else {
  server.listen({
    onUnhandledRequest: 'warn' // More lenient in local dev
  });
}

Lifecycle Events

Monitor request interception and handler execution.

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:

// Log all intercepted requests
server.events.on('request:start', ({ request, requestId }) => {
  console.log(`Intercepting: ${request.method} ${request.url} (${requestId})`);
});

// Track unhandled requests for debugging
server.events.on('request:unhandled', ({ request, requestId }) => {
  console.warn(`Unhandled request: ${request.method} ${request.url} (${requestId})`);
  
  // Could help identify missing mocks
  if (request.url.includes('/api/')) {
    console.error('Missing API mock for:', request.url);
  }
});

// Performance monitoring
server.events.on('response:mocked', ({ response, request, requestId }) => {
  console.log(`Mocked response (${response.status}) for ${request.url} (${requestId})`);
});

// Test debugging
if (process.env.NODE_ENV === 'test') {
  server.events.on('request:match', ({ request, requestId }) => {
    console.debug(`Test mock matched: ${request.url} (${requestId})`);
  });
}

// Request analytics
const requestStats = {
  total: 0,
  mocked: 0,
  bypassed: 0
};

server.events.on('request:start', () => {
  requestStats.total++;
});

server.events.on('response:mocked', () => {
  requestStats.mocked++;
});

server.events.on('response:bypass', () => {
  requestStats.bypassed++;
});

// Cleanup listeners
afterAll(() => {
  server.events.removeAllListeners();
});

Advanced Node.js Patterns

Handle complex Node.js scenarios and integrations.

// Dynamic handler loading based on environment
async function setupDynamicServer() {
  const baseHandlers = await import('./mocks/base-handlers');
  
  let environmentHandlers = [];
  if (process.env.NODE_ENV === 'test') {
    environmentHandlers = await import('./mocks/test-handlers');
  } else if (process.env.NODE_ENV === 'development') {
    environmentHandlers = await import('./mocks/dev-handlers');
  }
  
  const server = setupServer(
    ...baseHandlers.default,
    ...environmentHandlers.default
  );
  
  return server;
}

// Configuration-driven handler setup
interface MockConfig {
  enabled: boolean;
  handlers: string[];
  strictMode: boolean;
}

async function setupConfiguredServer(config: MockConfig) {
  if (!config.enabled) {
    return null;
  }
  
  const handlers = await Promise.all(
    config.handlers.map(path => import(path))
  );
  
  const server = setupServer(
    ...handlers.flatMap(module => module.default)
  );
  
  server.listen({
    onUnhandledRequest: config.strictMode ? 'error' : 'warn'
  });
  
  return server;
}

// Express.js integration
import express from 'express';

const app = express();
const server = setupServer(...handlers);

// Start MSW before starting Express server
server.listen();

app.listen(3000, () => {
  console.log('Server running with MSW interception');
});

// Graceful shutdown
process.on('SIGTERM', () => {
  server.close();
  process.exit(0);
});

Types

// Setup types
interface SetupServer {
  listen(options?: ListenOptions): void;
  close(): 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 ListenOptions {
  onUnhandledRequest?: 
    | 'bypass' 
    | 'warn' 
    | 'error' 
    | ((request: Request, print: { warning(): void; error(): void }) => void);
}

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

// Event types (same as browser but specific to Node.js context)
interface LifeCycleEventsMap {
  'request:start': [request: Request];
  'request:match': [request: Request];
  'request:unhandled': [request: Request];
  'response:mocked': [response: Response, requestId: string];
  'response:bypass': [response: Response, requestId: string];
}

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