Describing REST API

Learn how to describe REST API with Mock Service Worker.

Import

MSW provides a designated http namespace for describing HTTP requests. We will use that namespace to describe what requests to intercept and how to respond to them.

Import the http namespace from the msw package:

// src/mocks/handlers.js
import { http } from 'msw'
 
export const handlers = []

Although GraphQL is often implemented over HTTP, we highly recommend using the designated graphql namespace for describing GraphQL APIs.

Request handler

Next, we will create a Request handler. Every method in the http namespace allows us to create a request handler corresponding to an HTTP request method:

http[method](predicate, resolver)

Request handlers are functions that allow you to Intercept requests and Mock responses.

In this tutorial, we will describe a basic RESTful API that has the following endpoints:

  • GET /posts, to return all existing posts;
  • POST /posts, to create a new post;
  • DELETE /posts/:id, to delete a post by ID.

Let’s start by creating a request handler for the GET /posts requests.

Call http.get() to declare your first request handler

// src/mocks/handlers.js
import { http } from 'msw'
 
export const handlers = [
  // By calling "http.get()" we're instructing MSW
  // to capture all outgoing "GET /posts" requests
  // and execute the given response resolver when they
  // happen.
  http.get('/posts', () => {
    // Response resolver allows you to react to captured requests,
    // respond with mock responses or passthrough requests entirely.
    // For now, let's just print a message to the console.
    console.log('Captured a "GET /posts" request')
  }),
]

Following the same principle, add request handlers for the remaining endpoints:

// src/mocks/handlers.js
import { http } from 'msw'
 
export const handlers = [
  http.get('/posts', () => {
    console.log('Captured a "GET /posts" request')
  }),
  http.post('/posts', () => {
    console.log('Captured a "POST /posts" request')
  }),
  http.delete('/posts/:id', ({ params }) => {
    console.log(`Captured a "DELETE /posts/${params.id}" request`)
  }),
]

With these handlers defined, MSW will intercept the respective requests but won’t do anything to respond to them just yet.

Response resolver

Response resolver is the second argument to the request handler that decides how to handle the intercepted request. There are multiple things you can do with such a request: respond with a mock response, perform it as-is, perform a proxy request and augment the original response, etc. You can always learn more about response resolver on this pages:

In this tutorial, we will be responding to the intercepted requests with mock responses.

Mocking responses

To respond to an intercepted request, construct a valid Fetch API Response instance and return it from the corresponding request handler. Although you can work with the Response instance directly, we highly recommend using the HttpResponse class provided by the library.

Import the HttpResponse class from msw:

import { http, HttpResponse } from 'msw'

Learn about what HttpResponse is and why you should use it over the standard Response in here.

Next, let’s construct the mocked response to return the list of all posts.

Return a mocked HttpResponse from the GET /posts resolver:

// src/mocks/handlers.js
import { http, HttpResponse } from 'msw'
 
// Let's keep a map of all existing posts in memory.
// At the beginning, this list is empty as we have no posts.
const allPosts = new Map()
 
export const handlers = [
  http.get('/posts', () => {
    // Construct a JSON response with the list of all posts
    // as the response body.
    return HttpResponse.json(Array.from(allPosts.values()))
  }),
 
  // ...the other request handlers.
]

Using the HttpResponse.json() static method, we are returning an application/json response from this resolver to be used as the mocked response.

Reading request body

In the POST /posts handler, let’s read the request body and push the new post to the allPosts map. We can get the reference to the intercepted request instance as the request argument to the response resolver function.

Read the request body using the request argument:

// src/mocks/handlers.js
import { http, HttpResponse } from 'msw'
 
const allPosts = new Map()
 
export const handlers = [
  http.post('/posts', async ({ request }) => {
    // Read the intercepted request body as JSON.
    const newPost = await request.json()
 
    // Push the new post to the map of all posts.
    allPosts.set(newPost.id, newPost)
 
    // Don't forget to declare a semantic "201 Created"
    // response and send back the newly created post!
    return HttpResponse.json(newPost, { status: 201 })
  }),
]

Reading path parameters

Lastly, let’s imlpement the DELETE /posts/:id resolver that will lookup and delete a post from allPosts by its ID. The path parameters of the request URL are parsed and stored as the params argument to the response resolver function.

Read the id path parameter using the params argument

// src/mocks/handlers.js
import { http, HttpResponse } from 'msw'
 
const allPosts = new Map()
 
export const handlers = [
  http.delete('/posts/:id', ({ params }) => {
    // All request path params are provided in the "params"
    // argument of the response resolver.
    const { id } = params
 
    // Let's attempt to grab the post by its ID.
    const deletedPost = allPosts.get(id)
 
    // Respond with a "404 Not Found" response if the given
    // post ID does not exist.
    if (!deletedPost) {
      return new HttpResponse(null, { status: 404 })
    }
 
    // Delete the post from the "allPosts" map.
    allPosts.delete(id)
 
    // Respond with a "200 OK" response and the deleted post.
    return HttpResponse.json(deletedPost)
  }),
]

Response resolver provide you with much more useful data about the intercepted request, such as request cookies, GraphQL query and variables, and more. Learn more about the Response resolvers to unlock their full potential.

Reading request cookies

Access the parsed value of the request’s Cookie header as the cookies key on the response resolver object argument:

http.get('/user', ({ cookies }) => {
  const { session } = cookies
 
  if (!session) {
    return new HttpResponse(null, { status: 401 })
  }
})

Next steps

Integrations

Once you have described the network you want, integrate it into any environment in your application.

Recipes

Learn about other ways to work with requests and responses by browsing our recipes.