Type-safe API mocking with Mock Service Worker and TypeScript
Tags: testing, typescript, api, mocking
Published: 2021-02-28 14:10:14

> **NOTE:** This article is about MSW v1. We released [MSW v2](https://mswjs.io/blog/introducing-msw-2.0), which brings improvements to type-safety along with other features and bug fixes. Please read the [Using with TypeScript](https://mswjs.io/docs/best-practices/typescript) page for type-safe API mocking on MSW v2. Thank you!

[**Mock Service Worker**](https://mswjs.io/) is a seamless API mocking library for browser and Node.js. It uses Service Worker API to intercept requests on the network level, meaning no more stubbing of "fetch", "axios", or any other request issuing client. It provides a first-class experience when mocking REST and GraphQL API, and allows you to reuse the same mocks for testing, development, and debugging.

> Watch this 4 minutes tutorial on mocking a basic REST API response with Mock Service Worker to get a better understanding of how this library works and feels:

{% youtube e8lbbqSa4e4 %}

Today we're going to have a practical dive-in into adding TypeScript to your API mocking experience to bring it one step further.

### Why annotate mocks?

The mocks you write are a part of your application like any other piece of logic. Having a type validation is one of the cheapest and most efficient ways to ensure your mocks satisfy the data expectations towards them.

---

# REST API

Each [REST request handler](https://mswjs.io/docs/api/rest) has the following type signature:

```ts
type RestHandler = <RequestBody, ResponseBody, RequestParams>(mask, resolver) => MockedResponse
```

This allows us to annotate three things in our REST API handlers:

1. Request body type.
1. Response body type.
1. Request parameters.

Let's take a look at the `UPDATE /post/:postId` request that utilizes all three said generics:

```ts
import { rest } from 'msw'

// Describe the shape of the "req.body".
interface UpdatePostRequestBody {
  title: string
  viewsCount: string
}

// Describe the shape of the mocked response body.
interface UpdatePostResponseBody {
  updatedAt: Date
}

// Describe the shape of the "req.params".
interface UpdatePostRequestParams {
  postId: string
}

rest.update
  <UpdatePostRequestBody, UpdatePostResponseBody, UpdatePostRequestParams>(
  '/post/:postId',
  (req, res, ctx) => {
    const { postId } = req.params
    const { title, viewsCount } = req.body

    return res(
      ctx.json({
        updatedAt: Date.now()
      })
    )
  })
```

> The same generics apply to any `rest` request handler: `rest.get()`, `rest.post()`, `rest.delete()`, etc.

# GraphQL API

A type signature for the [GraphQL handlers](https://mswjs.io/docs/api/graphql) is:

```ts
type GraphQLHandler = <Query, Variables>(args) => MockedResponse
```

This means we can annotate the `Query` type (what gets returned in the response) and the `Variables` of our query.

Let's take a look at some concrete examples. 

## GraphQL queries

```ts
import { graphql } from 'msw'

// Describe the payload returned via "ctx.data".
interface GetUserQuery {
  user: {
    id: string
    firstName: string
    lastName: string
  }
}

// Describe the shape of the "req.variables" object.
interface GetUserQueryVariables {
  userId: string
}

graphql.query
  <GetUserQuery, GetUserQueryVariables>(
  'GetUser',
  (req, res, ctx) => {
    const { userId } = req.variables

    return res(
      ctx.data({
        user: {
          id: userId,
          firstName: 'John',
          lastName: 'Maverick'
        }
      })
    )
  })
```

## GraphQL mutations

Now, let's apply the same approach to a GraphQL mutation. In the case below we're having a `UpdateArticle` mutation that updates an article by its ID.

```ts
import { graphql } from 'msw'

interface UpdateArticleMutation {
  article: {
    title: string
    updatedAt: Date
  }
}

interface UpdateArticleMutationVariables {
  title: string
}

graphql.mutation
  <UpdateArticleMutation, UpdateArticleMutationVariables>(
  'UpdateArticle',
  (req, res, ctx) => {
    const { title } = req.variables

    return res(
      ctx.data({
        article: {
          title,
          updatedAt: Date.now()
        }
      })
    )
  })
```

## GraphQL operations

When it comes to capturing multiple GraphQL operations regardless of their kind/name, the [`graphql.operation()`](https://mswjs.io/docs/api/graphql/operation) truly shines. Although the nature of the incoming queries becomes less predictable, you can still specify its `Query` and `Variables` types using the handler's generics.

```ts
import { graphql } from 'msw'

type Query = 
  | { user: { id: string } }
  | { article: { updateAt: Date } }
  | { checkout: { item: { price: number } } }

type Variables = 
  | { userId: string }
  | { articleId: string }
  | { cartId: string }

graphql.operation<Query, Variables>((req, res, ctx) => {
  // In this example we're calling an abstract
  // "resolveOperation" function that returns
  // the right query payload based on the request.
  return res(ctx.data(resolveOperation(req)))
})
```

## Bonus: Using with GraphQL Code Generator

My absolute favorite setup for mocking GraphQL API is when you add [GraphQL Code Generator](https://graphql-code-generator.com) to the mix.

> GraphQL Code Generator is a superb tool that allows you to generate type definitions from your GraphQL schema, but also from the exact queries/mutations your application makes.

Here's an example of how to integrate the types generated by GraphQL Codegen into your request handlers:

```ts
import { graphql } from 'msw'
// Import types generated from our GraphQL schema and queries.
import { GetUserQuery, GetUserQueryVariables } from './types'

// Annotate request handlers to match 
// the actual behavior of your application.
graphql.query<GetUserQuery, GetUserQueryVariables>('GetUser', (req, res, ctx) => {})
```

With your data becoming the source of truth for your request handlers, you're always confident that your mocks reflect the actual behavior of your application. You also remove the need to annotate queries manually, which is a tremendous time-saver!

---

# Advanced usage

We've covered most of the common usage examples above, so let's talk about those cases when you abstract, restructure and customize your mocking setup. 

## Custom response resolvers

It's not uncommon to isolate a response resolver logic into a higher-order function to prevent repetition while remaining in control over the mocked responses. 

This is how you'd annotate a custom response resolver:

```ts
// src/mocks/resolvers.ts
import type { ResponseResolver, RestRequest, RestContext } from 'msw'

interface User {
  firstName: string
  lastName: string
}

export const userResolver = (user: User | User[]): ResponseResolver<RestRequest, RestContext, User> => {
  return (req, res, ctx) => {
    return res(ctx.json(user))
  }
})
```

```ts
import { rest } from 'msw'
import { userResolver } from './resolvers'
import { commonUser, adminUser } from './fixtures'

rest.get('/user/:userId', userResolver(commonUser))
rest.get('/users', userResolver([commonUser, adminUser])
```

## Custom response transformers

You can create custom context utilities on top of [response transformers](https://mswjs.io/docs/basics/response-transformer). 

Here's an example of how to create a custom response transformer that uses the `json-bigint` library to support BigInt in the JSON body of your mocked responses. 

```ts
// src/mocks/transformers.ts
import * as JsonBigInt from 'json-bigint'
import { ResponseTransformer, context, compose } from 'msw'

// Here we're creating a custom context utility
// that can handle a BigInt values in JSON.
export const jsonBigInt =
  (body: Record<string, any>): ResponseTransformer => {
    return compose(
      context.set('Content-Type', 'application/hal+json'),
      context.body(JsonBigInt.stringify(body))
    )
  }
```

> Note how you can compose your custom response transformer's logic by utilizing the `compose` and `context` exported from MSW. 

You can use that `jsonBigInt` transformer when composing mocked responses in your handlers:

```ts
import { rest } from 'msw'
import { jsonBigInt } from './transformers'

rest.get('/stats', (req, res, ctx) => {
  return res(
    // Use the custom context utility the same way
    // you'd use the default ones (i.e. "ctx.json()").
    jsonBigInt({
      username: 'john.maverick',
      balance: 1597928668063727616
    })
  )
})
```

---

## Afterword

Hope you find this article useful and learn a thing or two about improving your mocks by covering them with type definitions—either manual or generated ones.

There can be other scenarios when you may find yourself in need to cover your mocks with types. Explore what type definitions MSW exports and take a look at the [library's implementation](https://github.com/mswjs/msw) for reference.

Share this article with your colleagues and give it a shoutout on Twitter, I'd highly appreciate it! Thank you.

### Useful resources

- [**Mocking a basic HTTP response (YouTube)**](https://www.youtube.com/watch?v=e8lbbqSa4e4)
- [Mock Service Worker documentation](https://mswjs.io/)
- [GitHub repository](https://github.com/mswjs/msw)