Skip to content

GraphQLWsLink does not work together with fetchMore #12479

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
sebakerckhof opened this issue Mar 25, 2025 · 8 comments
Open

GraphQLWsLink does not work together with fetchMore #12479

sebakerckhof opened this issue Mar 25, 2025 · 8 comments
Labels
ℹ needs-more-info Needs more information to determine root cause 🥀 needs-reproduction

Comments

@sebakerckhof
Copy link
Contributor

Issue Description

I'm using the GraphQLWsLink to query data over a websocket.

This works fine, but it does not seem to work in combination with fetchMore.

Image

The image above shows 2 calls. The original call (via useQuery), which returns the data succesfully.

Then we see a second call being launched via fetchMore. The operation is requested and the server returns the results, but the data returned from useQuery is not updated. The results are also not added to the apollo client cache.

This does work with a regular HttpLink.

Link to Reproduction

Not possible due to dependency on a public graphql-ws server...

Reproduction Steps

No response

@apollo/client version

3.13.5

@jerelmiller
Copy link
Member

Hey @sebakerckhof 👋

We could really use a reproduction of some kind because its difficult to tell what the differences are. Both HttpLink and GraphQLWsLink operate by sending values through the observables returned from the link chain, so the transport shouldn't matter as long as the value emitted through the link chain is correct.

We don't need a reproduction of your system, just something that demonstrates the bug. Could you perhaps inspect the value emitted from each terminating link to see if there are differences there? Try adding this in your link chain to see the values emitted from those terminating links:

const loggerLink = new ApolloLink((operation, forward) => {
  return forward(operation).map((result) => {
    console.log(result);

    return result;
  })
})

Try this both with HttpLink and GraphQLWsLink to see if there are differences in that result.

@jerelmiller jerelmiller added 🥀 needs-reproduction ℹ needs-more-info Needs more information to determine root cause 🍳 breaking change 🏓 awaiting-contributor-response requires input from a contributor and removed 🍳 breaking change labels Mar 25, 2025
@sebakerckhof
Copy link
Contributor Author

The loggerlink returns the exact same data for both httplink and graphqlwslink

What I also notice is that the query that executed via fetchMore does not get completed (complete message send from client to server) when the component unmounts. Only the initial query gets completed.

@phryneas
Copy link
Member

Hmm, that sounds like for some reason the GraphQLWS-Server you are using is not sending the complete event after the next event.

Looking at their code, that should always be sent - except if you are maybe using a different server implementation?

Generally: Do you see the subscribe event (browser->server), followed by a next event (server->browser) followed by a completed event (server->browser), all with the same id, when you look into your network devtools?

@sebakerckhof
Copy link
Contributor Author

No, the complete event should only be send from server to browser if there are no further results.
We're using graphql-ws to implement a livequery server via CDC/Debezium. So in that case, the server can send consecutive next events. It's up to the client to unsubscribe by sending a complete (as mentioned in the graphql-ws spec). All of that works fine with regular queries and when we change variables to a query.

It only seems not to work in combination with fetchMore.

@phryneas
Copy link
Member

phryneas commented Mar 26, 2025

fetchMore is meant to make a single query (like "give me the next page") that at some point finishes, usually after the first next call, or if you use @defer, once all deferred chunks have arrived. It's not meant for ongoing data delivery - if that worked, it's purely accidental.

Are you sure you don't want to use subscribeToMore?

@phryneas
Copy link
Member

phryneas commented Mar 26, 2025

Just to be clear here - the GraphQL-WS-spec is just the transport protocol spec, it doesn't override the GraphQL spec itself, it just describes how to transport results of the GraphQL spec.

The GraphQL spec defines the result of executing a query as a single map object containing data and error properties:
https://spec.graphql.org/October2021/#sec-Query

ExecuteQuery(query, schema, variableValues, initialValue)

Let queryType be the root Query type in schema.
Assert: queryType is an Object type.
Let selectionSet be the top level Selection Set in query.
Let data be the result of running [ExecuteSelectionSet](https://spec.graphql.org/October2021/#ExecuteSelectionSet())(selectionSet, queryType, initialValue, variableValues) normally (allowing parallelization).
Let errors be any field errors produced while executing the selection set.
Return an unordered map containing data and errors.

There is no wiggle room for more than one result here - this will slightly change with the @defer and @stream directives (those are the "streaming directives" mentioned in the GraphQL-WS-spec - we currently support @defer in a pre-stable-spec version), but not in the way you seem to be using it here.

@github-actions github-actions bot removed the 🏓 awaiting-contributor-response requires input from a contributor label Mar 27, 2025
@sebakerckhof
Copy link
Contributor Author

sebakerckhof commented Mar 27, 2025

I was indeed hoping to use similar mechanisms as @defer to update query results.
I think there are a couple of arguments to be made as to why this would be preferable over using subscriptions or subscribeToMore, but that would lead us a bit too much off topic.

So if we forget about that part, I think there's still an issue here (although not tied to the graphql-ws transport link), since fetchMore also doesn't seem to work well together with @defer.

I tried making a minimal reproduction to show this: https://github.com/sebakerckhof/graphql-defer-test
The client requests a query with a deferred fragment. The server responds the initial results immediately and has a 5 second wait before sending the deferred parts to make the issue more visible.
This works fine for the initial query, but this doesn't seem to work correctly when using fetchMore.

@phryneas
Copy link
Member

phryneas commented Mar 27, 2025

Yes, you're right, there's a bug in there - we're tracking that one over in #12257.

We'll need to revisit the whole implementation of @defer once graphql 17 reaches final and the @defer spec becomes "final enough", which is why we have postponed that one for now (we're entering the final stretch of work for Apollo Client 4.0, so we're currently very focused on that).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ℹ needs-more-info Needs more information to determine root cause 🥀 needs-reproduction
Projects
None yet
Development

No branches or pull requests

3 participants