Mocking responses

Learn how to mock HTTP responses.

Basics

Mock Service Worker respects the WHATWG Fetch API specification, which means that the mocked responses you construct are the same responses you would receive when making a fetch call. If you are comfortable with the Response class, you can skip everything past this section—you know how to declare responses already.

To respond to an intercepted request, return a valid Response instance from the matching request handler:

import { http } from 'msw'
 
export const handlers = [
  // Intercept the "GET /resource" request.
  http.get('/resource', () => {
    // And respond with a "text/plain" response
    // with a "Hello world!" text response body.
    return new Response('Hello world!')
  }),
]

You don’t need to import the Response class because it’s a part of the global Fetch API in the browser and modern versions of Node.js (v17+). The library supports any variations of the Response instances, including shorthand responses created via methods like Response.json() or Response.error().

Using HttpResponse class

Although you can use plain Fetch API responses, it’s highly recommended you use the custom HttpResponse class provided by the library. There are two reasons to prefer HttpResponse over the native Response:

  1. The HttpResponse class encapsulates useful shorthand methods for declaring responses, like HttpResponse.json(), HttpResponse.xml(), HttpResponse.formData(), etc.
  2. Unlike the native Response class, the HttpResponse class enables support for mocking response cookies by setting the Set-Cookie header on the mocked response.

Here’s the same GET /resource handler but using the HttpResponse class:

// 1. Import the "HttpResponse" class from the library.
import { http, HttpResponse } from 'msw'
 
export const handlers = [
  http.get('/resource', () => {
    // 2. Return a mocked "Response" instance from the handler.
    return HttpResponse.text('Hello world!')
  }),
]

HttpResponse is 100% compatible with the native Response class. Under the hood, HttpResponse returns a plain Fetch API Response instance.

Mocking status code and text

Provide the status and/or statusText property in the response initializer object to specify a mocked response status or response status code, respectively.

import { http, HttpResponse } from 'msw'
 
export const handlers = [
  http.get('/apples', () => {
    return new HttpResponse(null, {
      status: 404,
      statusText: 'Out Of Apples',
    })
  }),
]

Mocking headers

Provide the headers property in the response initializer object to specify mocked response headers.

import { http, HttpResponse } from 'msw'
 
export const handlers = [
  http.post('/auth', () => {
    return new HttpResponse(null, {
      headers: {
        'Set-Cookie': 'mySecret=abc-123',
        'X-Custom-Header': 'yes',
      },
    })
  }),
]

Learn more about constructing Headers.

Mocking body

You can respond to requests with various response body types: String, Blob, FormData, ReadableStream, and others (see Fetch API Response for supported response body types).

Below, let’s take a look at how to mock some of the most common HTTP response bodies.

Text responses

The most basic response in HTTP is a text response. Provide the text string you wish to respond with as an argument to the HttpResponse constructor to create a mocked text response:

import { http, HttpResponse } from 'msw'
 
export const handlers = [
  http.get('/name', () => {
    return new HttpResponse('John')
  }),
]

You can also use HttpResponse.text() shorthand static method.

JSON responses

A much more common response body type is JSON. Provide the JSON you wish to respond with as an argument to the HttpResponse.json() static method to create a mocked JSON response:

import { http, HttpResponse } from 'msw'
 
export const handlers = [
  http.post('/auth', () => {
    // Note that you DON'T have to stringify the JSON!
    return HttpResponse.json({
      user: {
        id: 'abc-123',
        name: 'John Maverick',
      },
    })
  }),
]

We recommend using the HttpResponse.json() shorthand static method to automatically keep the Content-Type and Content-Length response headers in-sync with the response JSON body you’re using.

Stream responses

You can respond with a ReadableStream to stream any data back to the client.

Streaming

Respond with a `ReadableStream`.

Other responses

There are many other response body types you can describe with MSW, like XML, Blob, ArrayBuffer, or FormData. Please refer to the HttpResponse API to learn more about using other response body types.

HttpResponse

API reference for the `HttpResponse` class.

Mocking error responses

Construct and return a valid error response from a response resolver to emulate an error response.

import { http, HttpResponse } from 'msw'
 
export const handlers = [
  http.get('/user', () => {
    // Respond with "401 Unauthorized" to "GET /user" requests.
    return new HttpResponse(null, { status: 401 })
  }),
]

Mocking network errors

Use the Response.error() or HttpResponse.error() static method to raise a network error. Unlike an error response, a network error will halt the request execution and will mark that request as failed. In practice, you may experience network errors when constructing an invalid request or requesting hostnames that cannot be resolved.

import { http, HttpResponse } from 'msw'
 
export const handlers = [
  http.post('/checkout/cart', () => {
    return HttpResponse.error()
  }),
]

Throwing responses

You can throw a Response instance at any point in the response resolver. When that happens, the request handling short-circuits, and the thrown response is returned as the mocked response.

import { http, HttpResponse } from 'msw'
 
http.post('/login', ({ request }) => {
  if (!request.headers.has('cookie')) {
    throw new HttpResponse(null, { status: 400 })
  }
})

This is particularly handy to implement a middleware pattern when handling requests.

async function isAuthenticated(request) {
  if (!request.headers.has('cookie')) {
    throw new HttpResponse(null, { status: 400 })
  }
}
 
http.post('/login', async ({ request }) => {
  await isAuthenticated(request)
 
  return new HttpResponse(null, {
    status: 302,
    headers: {
      Location: '/dashboard',
    },
  })
})

Note that any non-response exceptions will be translated to 500 Internal Server Error responses, similar to an unhandled rejection happening on server runtime.