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

http-handlers.mddocs/

HTTP Handlers

HTTP handlers provide Express-like routing for intercepting and mocking HTTP requests with support for path parameters, query strings, and custom predicates.

Capabilities

HTTP Request Handler Function

Creates handlers for specific HTTP methods and URL patterns.

/**
 * Creates an HTTP request handler for a specific method and URL pattern
 * @param predicate - URL pattern (string/RegExp) or custom predicate function
 * @param resolver - Function that returns a Response for matching requests
 * @param options - Optional configuration including 'once' for single-use handlers
 * @returns HttpHandler instance
 */
interface HttpRequestHandler {
  <Params extends PathParams<keyof Params> = PathParams, 
   RequestBodyType extends DefaultBodyType = DefaultBodyType,
   ResponseBodyType extends DefaultBodyType = undefined>(
    predicate: HttpRequestPredicate<Params>,
    resolver: HttpResponseResolver<Params, RequestBodyType, ResponseBodyType>,
    options?: RequestHandlerOptions
  ): HttpHandler;
}

type HttpRequestPredicate<Params> = 
  | string 
  | RegExp 
  | HttpCustomPredicate<Params>;

type HttpCustomPredicate<Params = Record<string, string>> = (info: {
  request: Request;
  parsedResult: HttpRequestParsedResult<Params>;
}) => boolean;

interface HttpRequestParsedResult<Params = Record<string, string>> {
  match: Match<Params>;
  query: RequestQuery;
}

type HttpResponseResolver<
  Params extends PathParams<keyof Params> = PathParams,
  RequestBodyType extends DefaultBodyType = DefaultBodyType,
  ResponseBodyType extends DefaultBodyType = DefaultBodyType
> = (info: HttpRequestResolverExtras<Params>, request: Request, requestBody: RequestBodyType) => 
  | Response 
  | Promise<Response>
  | HttpResponse<ResponseBodyType>
  | Promise<HttpResponse<ResponseBodyType>>;

interface HttpRequestResolverExtras<Params> {
  request: Request;
  params: Params;
  cookies: Record<string, string>;
}

HTTP Method Handlers

The http namespace provides handlers for all standard HTTP methods.

const http: {
  /** Handle requests with any HTTP method */
  all: HttpRequestHandler;
  /** Handle HTTP GET requests */
  get: HttpRequestHandler;
  /** Handle HTTP POST requests */
  post: HttpRequestHandler;
  /** Handle HTTP PUT requests */
  put: HttpRequestHandler;
  /** Handle HTTP DELETE requests */
  delete: HttpRequestHandler;
  /** Handle HTTP PATCH requests */
  patch: HttpRequestHandler;
  /** Handle HTTP HEAD requests */
  head: HttpRequestHandler;
  /** Handle HTTP OPTIONS requests */
  options: HttpRequestHandler;
};

Usage Examples:

import { http, HttpResponse } from "msw";

// Basic GET handler with static response
http.get('/api/users', () => {
  return HttpResponse.json([
    { id: 1, name: 'John Doe' },
    { id: 2, name: 'Jane Smith' }
  ]);
});

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

// Handler with path parameters
http.get('/api/users/:userId', ({ params }) => {
  const { userId } = params;
  
  return HttpResponse.json({
    id: userId,
    name: `User ${userId}`
  });
});

// Handler with query parameters
http.get('/api/search', ({ request }) => {
  const url = new URL(request.url);
  const query = url.searchParams.get('q');
  const limit = url.searchParams.get('limit') || '10';
  
  return HttpResponse.json({
    query,
    results: [],
    limit: parseInt(limit)
  });
});

// Handler with cookies
http.get('/api/profile', ({ request, cookies }) => {
  const sessionId = cookies.sessionId;
  
  if (!sessionId) {
    return HttpResponse.json(
      { error: 'Unauthorized' },
      { status: 401 }
    );
  }
  
  return HttpResponse.json({ user: 'profile data' });
});

// RegExp pattern handler
http.get(/\/api\/files\/(.+)\.json$/, ({ request }) => {
  const url = new URL(request.url);
  const filename = url.pathname.match(/\/api\/files\/(.+)\.json$/)?.[1];
  
  return HttpResponse.json({ filename, content: {} });
});

// Custom predicate handler
http.post('/api/upload', ({ request }) => {
  const contentType = request.headers.get('content-type');
  
  if (!contentType?.includes('multipart/form-data')) {
    return HttpResponse.json(
      { error: 'Invalid content type' },
      { status: 400 }
    );
  }
  
  return HttpResponse.json({ success: true });
}, {
  predicate: ({ request }) => {
    return request.headers.get('content-type')?.includes('multipart/form-data') || false;
  }
});

// One-time handler (auto-removes after first match)
http.get('/api/setup', () => {
  return HttpResponse.json({ initialized: true });
}, { once: true });

// Handler with different response based on request
http.all('/api/echo', async ({ request }) => {
  const method = request.method;
  const body = method !== 'GET' ? await request.text() : null;
  
  return HttpResponse.json({
    method,
    url: request.url,
    headers: Object.fromEntries(request.headers.entries()),
    body
  });
});

Path Parameter Extraction

MSW automatically extracts path parameters from URL patterns using Express-style syntax.

type PathParams<T extends string = string> = Record<T, string>;

interface Match<Params = Record<string, string>> {
  matches: boolean;
  params: Params;
}

Supported Path Patterns:

// Named parameters
http.get('/users/:userId', ({ params }) => {
  // params.userId is available
});

// Multiple parameters
http.get('/users/:userId/posts/:postId', ({ params }) => {
  // params.userId and params.postId are available
});

// Optional parameters
http.get('/api/posts/:id?', ({ params }) => {
  // params.id may be undefined
});

// Wildcard patterns
http.get('/assets/*', ({ request }) => {
  // Matches any path under /assets/
});

// RegExp with capture groups
http.get(/^\/api\/v(\d+)\/users\/(\w+)$/, ({ request }) => {
  const matches = new URL(request.url).pathname.match(/^\/api\/v(\d+)\/users\/(\w+)$/);
  const version = matches?.[1];
  const userId = matches?.[2];
});

Query Parameter Access

Access query parameters through the standard URL API in request handlers.

http.get('/api/search', ({ request }) => {
  const url = new URL(request.url);
  const searchParams = url.searchParams;
  
  // Get single values
  const query = searchParams.get('q');
  const page = searchParams.get('page') || '1';
  
  // Get all values for a parameter
  const tags = searchParams.getAll('tag');
  
  // Check if parameter exists
  const hasFilter = searchParams.has('filter');
  
  return HttpResponse.json({
    query,
    page: parseInt(page),
    tags,
    hasFilter
  });
});

Request Body Access

Access and parse request bodies using standard Request API methods.

// JSON body
http.post('/api/data', async ({ request }) => {
  const jsonData = await request.json();
  return HttpResponse.json({ received: jsonData });
});

// Form data
http.post('/api/form', async ({ request }) => {
  const formData = await request.formData();
  const username = formData.get('username');
  return HttpResponse.text(`Hello ${username}`);
});

// Text body
http.post('/api/text', async ({ request }) => {
  const textData = await request.text();
  return HttpResponse.text(`Received: ${textData}`);
});

// Binary data
http.post('/api/upload', async ({ request }) => {
  const arrayBuffer = await request.arrayBuffer();
  return HttpResponse.json({ size: arrayBuffer.byteLength });
});

// Stream body
http.post('/api/stream', async ({ request }) => {
  if (request.body) {
    const reader = request.body.getReader();
    // Process stream...
  }
  return HttpResponse.json({ processed: true });
});

Handler Options

Configure handler behavior with options object.

interface RequestHandlerOptions {
  /** Remove handler after first match */
  once?: boolean;
}
// One-time handler
http.get('/api/init', () => {
  return HttpResponse.json({ initialized: true });
}, { once: true });

Types

// Handler types
interface HttpHandler {
  method: HttpMethods | RegExp;
  predicate: HttpRequestPredicate<any>;
  resolver: HttpResponseResolver<any, any, any>;
  options: RequestHandlerOptions;
}

enum HttpMethods {
  GET = 'GET',
  POST = 'POST',
  PUT = 'PUT',
  DELETE = 'DELETE',
  PATCH = 'PATCH',
  HEAD = 'HEAD',
  OPTIONS = 'OPTIONS'
}

// Request types
type RequestQuery = Record<string, string | string[]>;

type DefaultBodyType = 
  | string 
  | number 
  | boolean 
  | null 
  | undefined 
  | ArrayBuffer 
  | Blob 
  | FormData 
  | ReadableStream;

type DefaultRequestMultipartBody = Record<string, string | File>;

// Response types  
type ResponseResolverReturnType<ResponseBodyType> =
  | Response
  | HttpResponse<ResponseBodyType>
  | Promise<Response>
  | Promise<HttpResponse<ResponseBodyType>>;

type AsyncResponseResolverReturnType<ResponseBodyType> = Promise<
  ResponseResolverReturnType<ResponseBodyType>
>;

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