Skip to content

fix(GraphQL): support application/graphql-response+json response content-type#2513

Merged
kettanaito merged 6 commits intomswjs:mainfrom
phryneas:pr/support-graphql-over-http-spec
Mar 17, 2026
Merged

fix(GraphQL): support application/graphql-response+json response content-type#2513
kettanaito merged 6 commits intomswjs:mainfrom
phryneas:pr/support-graphql-over-http-spec

Conversation

@phryneas
Copy link
Copy Markdown
Contributor

This would add support for the GraphQL over HTTP spec: https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#accept

The spec introduces a new mime type that future severs should be able to handle - 'application/graphql-response+json' instead of the traditional 'application/json'.

So right now, all tests using HttpResponse.json would need to switch over to a new constructing function.
My suggestion here would be to just have GraphQLHandler return based on a list of supportedMimeTypes and the priority specified by the GraphQL client.
Clients requesting 'application/json' would be served 'application/json' while clients requesting "application/graphql-response+json, application/json;q=0.9" would be served 'application/graphql-response+json'.

Note that it would be possible to still always return 'application/json' and simulate a server that only speaks the old protocol by specifying a supportedMimeTypes of ['application/json'].

This is still lacking tests, but there is a good chance you want completely different behaviour so I'm opening this as-is for discussion.

Comment thread src/core/handlers/GraphQLHandler.ts Outdated
'application/json',
]

const wrappedResolver: typeof resolver = (info) => {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sure there's a more elegant way to do this that I'm just not aware of....

@kettanaito
Copy link
Copy Markdown
Member

Hi, @phryneas. Thanks for opening this!

It would help a lot if we started from the intended usage experience. How do you see people using MSW to mock queries done via the GraphQL-over-HTTP spec? Let's design the ideal usage and then work down from there to see what implementation that would require.

From my limited understanding, all that should happen is that mocked responses from graphql.* handlers should now contain the content-type: application/graphql-response+json header. Does anything else change for the developer who wishes to mock that standard?

@kettanaito kettanaito changed the title feat(graphq): add support for graphql-over-http spec feat(GraphQL): support application/graphql-response+json requests Nov 23, 2025
@kettanaito kettanaito marked this pull request as ready for review March 17, 2026 10:08
@kettanaito
Copy link
Copy Markdown
Member

Update

Thanks for proposing this, @phryneas. I've refactored your approach, added tests, and now they are passing. Here's an overview of what I did.

  • Removed GraphQLHandler options (not a fan of options).
  • Refactored getAllAcceptedMimeTypes to adhere better to the RFC.
  • Removed resolver wrapping and instead extended the run() method to patch the mocked response, if any, there.

The behavior regarding the content-type and accept headers is now as follows:

  1. If request provides no accept, mocked responses are application/json.
  2. If request provides accept and lists application/graphql-response+json as the only header, mocked responses are application/graphql-response+json.
  3. If request provides accept and lists both application/graphql-response+json and application/json;q=0.9, mocked responses are application/graphql-response+json.
  4. If request provides accept and lists both application/graphql-response+json;q=0.9 and application/json (the new mime type with lower priority), mocked responses are application/json.

The response content type is fully controlled by the client, never the mock. The mock is basically a server and it responds with whatever content type the client says it accepts and prefers.

@phryneas, could you give this change a quick review to see if it complies with the expectations behind the new content type header? I'd be most grateful!

@kettanaito kettanaito changed the title feat(GraphQL): support application/graphql-response+json requests fix(GraphQL): support application/graphql-response+json response content-type Mar 17, 2026
kettanaito
kettanaito previously approved these changes Mar 17, 2026
Copy link
Copy Markdown
Member

@kettanaito kettanaito left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me. Would love to know what you think before releasing it.

@phryneas
Copy link
Copy Markdown
Contributor Author

phryneas commented Mar 17, 2026

It generally looks good, but as you removed the GraphQLHandler options, it's now impossible to mock an "old" server that didn't support application/graphql-response+json.
I'm not sure if that's good. In some tests you might want to test both a server that's not capable of the new format and a server that is, since responses from them are interpreted slightly differently.

One additional question I have after re-reading this after all that time: is it possible to mock these responses with a different response code? Where application/json can only respond with 200, application/graphql-response+json can also respond with other HTTP status codes, and tests might want to test for that.

@kettanaito
Copy link
Copy Markdown
Member

it's now impossible to mock an "old" server that didn't support application/graphql-response+json.

You can always do that by providing an explicit content-type headers on the mocked response though.

graphql.query('GetOldServer', () => {
  return HttpResponse.json({
    data: { ... }
  }, {
    headers: {
      // Regardless of what the client accepts, use this mime type.
      'content-type': 'application/json'
    }
  })
})

We just need to add a check for explicit content-type header and skip the decorating logic altogether. WYSIWYG when it comes to mocked responses, MSW must never override your choices.

One additional question I have after re-reading this after all that time: is it possible to mock these responses with a different response code? Where application/json can only respond with 200, application/graphql-response+json can also respond with other HTTP status codes, and tests might want to test for that.

This again sounds like what you should do in your handlers. MSW will not interfere if you decide to respond with a different status for your application/graphql-response+json responses. I think there's a false assumption that MSW is "smart" and should handle all inputs like the server does. It shouldn't. It's your network boundary and you model it to behave as you wish. Whatever you have in your mock is the truth, and you use that to reproduce different scenarios.

It's the same mental model as when it comes to putting business logic in your handlers. Don't.

it('handles unauthorized users', () => {
  server.use(
    http.get('/resource', () => {
      // Any request will be unauthorized.
      return new HttpResponse(null, { status: 401 })
    })
  )
})

You don't check for what request is. You always say "this is an unauthorized behavior branch". If you so choose to bring logic to your handler, you always can, but MSW won't try to guess anything for you.

@phryneas
Copy link
Copy Markdown
Contributor Author

With those responses, approved ✅

@kettanaito
Copy link
Copy Markdown
Member

Alright, pushed test + support for ignoring this logic when an explicit content-type is set on the mocked response so you can simulate old server scenarios.

@kettanaito kettanaito merged commit 4b8c330 into mswjs:main Mar 17, 2026
18 checks passed
@kettanaito
Copy link
Copy Markdown
Member

Released: v2.12.13 🎉

This has been released in v2.12.13.

Get these changes by running the following command:

npm i msw@latest

Predictable release automation by Release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants