msw
msw copied to clipboard
apollo-link-batch-http (batch) queries are not getting intercepted
Environment
Name | Version |
---|---|
msw | 0.24.2 |
browser | chrome Version 87.0.4280.88 (Official Build) (x86_64) |
OS | macOS Big Sur 11.0.1 (20B29) |
Request handlers
export const handlers = [
graphql.operation((req, res, ctx) => {
console.log({ req, res, ctx });
return res(
ctx.data({
user: {
username: 'test',
firstName: 'John',
},
})
);
}),
graphql.query('getTest', (req, res, ctx) => {
return res(
ctx.data({
user: {
username: 'test',
firstName: 'John',
},
})
);
}),
];
Actual request
https://www.apollographql.com/docs/link/links/batch-http/ Using Apollo Client to perform a normal batch query
ie.
import { BatchHttpLink } from "apollo-link-batch-http";
const link = new BatchHttpLink({ uri: "/graphql" });
Current behavior
batch queries are not getting intercepted but if I switch from BatchHttpLink
to a regular HttpLink
, the interception works as expected.
Expected behavior
batch queries get intercepted
the only way I got graphql batch requests to be intercepted, is by using rest with the full request URL, like so:
rest.post('https://my-graphql-server/endpoint', (req, res, ctx) => {
...
}
and then I have to manually analyze the req.body[] and handle/mock the request. meaning, I lose all the graphql declarative/functionality mswjs provides...
@antoniosZ Do you have a repro for this? I haven't tried this feature myself, but I imagine it might have to be linked? https://mswjs.io/docs/api/graphql/link#examples
@msutkowski I tried linking and that didn't help. I'll see if I can quickly create a repro
@msutkowski Unfortunately, I could not find a public graphql server to use for the repro, that supports query batching. please let me know if you have one available and then I can create the demo project that reproduces it... thank you
I'm not a graphQL user personally but I can try to take a look at this later and set up a server locally. It's possible that somebody will beat me to this but I'll try to get to it by tomorrow
Ah, the issue here is that msw
doesn't currently support batch operations. BatchLink
is sending over an array of operations, which isn't going to resolve. If you're able to, I'm sure @kettanaito would accept a PR if you have the time to implement this? If not, perhaps someone can jump in :)
@msutkowski this is exactly what is happening. It posts an array, instead of an object... I wish I could say I have the time to work on it, but I doubt I will be able to get to it.
@antoniosZ can you test it out against this preview of the build? https://ci.codesandbox.io/status/mswjs/msw/pr/513/builds/83287 - or npm i https://pkg.csb.dev/mswjs/msw/commit/d19bb4e8/msw
and let me know if it works as expected? thanks!
thank you @msutkowski but unfortunately, it does not work.
It appears that the problem is here: https://github.com/mswjs/msw/pull/513/files#diff-6178a21f02701c9f8fa7ae2cc995b08165615a3afddac155fb850e355269897aR154
@antoniosZ Sorry about that, there is a little more work to do here for handling the arrays. I'll get it taken care of today :)
thank you for working on this @msutkowski! Is there a new commit that you would like me to test?
@msutkowski We're interested in a resolution for this as well in our organisation.
Unfortunately we have found errors in TS integration beyond version 0.21.2, so we're fixed to that at the moment. We're considering forking from that version and then building on the work you've currently put in to resolve this. Have you any thoughts about or objections to this approach?
Many thanks!
Hello, I maintain a GraphQL client for Vue.js and used the following workaround for the batched requests test.
As @antoniosZ pointed out, it requires intercepting it as a rest request, but I utilized the handlers you typically define earlier
export const handlers = [
// some graphql handlers ...
// add this to the end of GQL handlers
rest.post('https://test.com/graphql', async (req, res) => {
if (!Array.isArray(req.body)) {
throw new Error('Unknown operation');
}
// map the requests to responses using their handlers
const data = await Promise.all(
req.body.map(async op => {
const partReq = { ...req, body: op };
const handler = handlers.find(h => h.test(partReq));
// no handler matched that operation
if (!handler) {
return Promise.reject(new Error(`Cannot handle operation ${op}`));
}
// execute and return the response-like object
return handler.run(partReq);
})
);
return res(res => {
res.headers.set('content-type', 'application/json');
// responses need extraction from the individual requests
// and we stitch them into a single array response
res.body = JSON.stringify(
data.map(d => {
return JSON.parse(d?.response?.body) || {};
})
);
return res;
});
}),
];
I realize this might not cover most cases, but works for me for now.
@msutkowski did some incredible job on the initial support phase. We've merged a major refactoring to how the request handlers work internally, so that pull request needs to be adjusted. I will try to resolve the conflicts and leave it at the same state for any volunteers to collaborate.
Thank you for your interest in this. We'd love to make batched operations support happen.
I'm experiencing the same issue, any fix coming up ?
Would also like a fix for this! Spent a good 2h on this before figuring it was a bug!
bump
Ok, so its been a year and still no fix?
Hi everyone! I was wondering if there are any plans to continue the work to support batched queries. Seems like the linked PR is kind of stale now.
We're loving msw for rest
APIs and spent a good few hours troubleshooting why the graphql
intercepters wouldn't work before landing on this issue. Right now we have to go back to use apollo's MockedProvider
but I'd love for this to go through, eventually!
bump
Hi all. Any progress on this?
Hey @kettanaito! First of all, thank you for all of the work you've put into this really useful tool!
I was wondering if this bug was on your roadmap to resolve, or if it was available for someone to pick up and complete? This thread seems to have stagnated (with a few requests for updates), and #513 seems to have gone stale.
If this is something you're still looking for help on, I may be able to dedicate some time to digging in on this -- having this resolved would save me a ton of time in my app!
@kettanaito any progress on this?
Sharing my thoughts on the batched GraphQl queries since I had a minute to look into this question.
Batched queries are non-standard
First, I can hardly see support for batched GraphQL queries landing in MSW for the following reason: query batching isn't a part of the GraphQL specification. It's a technique used by GraphQL clients to enhance performance. This means that each GraphQL client is free to implement and interpret batched queries as it wishes.
For example, Apollo and batch-execute
implement two entirely different syntaxes for batched queries:
// Apollo
[
{ data: { user: value },
{ data: { product: value },
{ data: { user: value }
]
// batch-execute
{
user_0: value,
product_0: value,
user_1: value
}
As one would expect from the lack of standard, the clients will dictate all sorts of formats that each client finds the best for them. MSW, however, cannot cater to particular clients, it's against our core philosophy. The library has shipped zero framework/client-specific code for 5 years, and I won't see that changed.
In practice, the lack of a standard batched query format means MSW has to guess what GraphQL client you may be using. Inevitably, this will lead to users demanding support for their client of choice and how batched queries are implemented there. This is a slippery slope of failed expectations I'd rather not see us even step a foot at.
This issue quickly becomes apparent once you try to apply a batched query resolution to the request/response contract. 1 request handler is responsible for controlling 1 request. Naturally, you want to keep the query/response collocation flat because that's how it is in your actual GraphQL server (batching is an entirely a GraphQL client's feature; the client wraps/unwraps the requests, your server doesn't know anything about those batches). Suddenly, a single request contains multiple GraphQL queries that have to be (1) parsed, (2) matched against the request handlers; (3) resolved. A single GraphQL request handler cannot do it so the next idea is to lift this resolution up to handleRequest
/getResponse
pipelines. But that doesn't work either because now we need to preemptively parse every HTTP request just to check if it's potentially a GraphQL request with a batched query even if you don't have any GraphQL handlers at all. This is an apparent design flaw and an indication that such logic doesn't belong to MSW.
How to handle batched queries then?
I suggest you detect and unwrap the batched queries in your network description. I highly suggest you do this on your end because:
- You know what GraphQL client you are using so you know which format of batched queries to expect in the intercepted request's body;
- You know that you are using GraphQL for a fact; MSW doesn't know that. It has no way of detecting that either (and it shouldn't do such things anyway).
I already have both use cases implemented and passing tests in here: https://github.com/mswjs/msw/pull/1982. Once this is finalized, I will add this as a recipe to the docs so it's clear how to approach mocking batched GraphQL queries.
Could graphql-over-http
serve as a standard?
@kalabro, we can but you have to consider that spec itself is a draft and can change as more and more people contribute and refine it. Batching is also not mentioned in their public draft, and given it's in the /rfc
directory, it seems it's only a proposal at this stage.
The important bit is this: you can achieve support for batched GraphQL queries trivially on your side. You are also in full context and awareness of what you're using so you don't have to guess, unlike MSW. This is a win-win for both sides and this is the direction we are taking on such features. I'm finalizing the tests and the updates to the docs to share more details on how to do that.
Interestingly enough, I can see that Apollo uses this array-of-queries while
batched-execute
prepends field aliases to support multiple same fields in a single query. At first, I liked the latter approach more because you get a single query that you can resolve against the schema and be done with it (with the client then mapping the right aliases to the right operations). But as I kept testing, I think I actually prefer the array-of-queries a bit more. You cannot resolve it against a schema manually but you can extract each individual query and resolve it against the list of request handlers in MSW and then return the accumulated response.
Released: v2.1.3 🎉
This has been released in v2.1.3!
Make sure to always update to the latest version (npm i msw@latest
) to get the newest features and bug fixes.
Predictable release automation by @ossjs/release.