Discussion: subscriptions API design improvements
See original GitHub issueI’ve been really happy with Apollo so far, and I’m delighted that it supports subscriptions, but the way subscriptions work in Apollo still seems half-baked to me, especially compared to what I was used to in Meteor. Here are some random thoughts that have been brewing in my mind. Everyone feel free to comment or suggest solutions!
Subscription updates can be missed while you’re waiting for query results
If your subscription variables depend on query results, you can’t initiate the subscription until the initial query is complete. This poses a problem: if any updates were published between the time the initial query was executed on the server and the client gets the result and initiates the subscription, the client will never get those updates.
This is why I think it would be better to use Meteor-style subscriptions that fetch and send initial values after the server starts listening to PubSub. This is definitely already possible, but it’s not recommended in the Apollo docs, and the danger of missed updates is not mentioned anywhere in the docs AFAIK. It would also take manual effort to send the initial update when the subscription starts; something systematic is desirable if possible.
For example, at the very least, PubSub could hang onto the last value published on each topic and (optionally) republish the last value when pubsub.asyncIterator
is called.
Or, pubsub.asyncIterator
could accept an initialValue
option, so that in our subscription resolvers, we could fetch the initial value and then pass it to pubsub.asyncIterator
. This would be less involved than setting up our own async iterator to yield the initial value and then yield the
rest from pubsub.asyncIterator
. (I assume at least that any time we create an async iterator of our own, we should take care to implement the return
and throws
methods to ensure that it gets shut down properly?)
What about standalone subscriptions?
The current design seems to assume we’ll always use a subscription with a corresponding query. But coming from Meteor, where you use subscribe by itself, and the subscription sends the initial data on startup, using a query and subscription together seems a little overcomplicated for some use cases.
If subscription updates contain objects with __typename
and id
, couldn’t Apollo update the cache automatically?
Right now developers have to write their own updateQuery
logic for each subscription, which is a bit cantankerous. But if the objects in a subscription update contain __typename
and id
, I’d think Apollo could apply the same normalization and cache update logic as it does for queries automatically, and not require developers to write any update logic of their own. Is there something I’m not thinking about that makes this difficult?
Updating deeply nested objects is awkward
This kind of goes along with the above. Let’s say my query is
query {
User {
id
name
Posts {
id
text
}
}
}
And let’s say I subscribe to updates on posts for the given post id
s in the query result.
When I get a post update, I have to merge it into the query shape, which is fairly tedious, something like the following code. It would be a lot simpler if I could just tell the Apollo cache to
replace its cached post with the given id with the updated version. (Which maybe is possible, but
I don’t know if it can really be done via subscribeToMore
.)
updateQuery(prev, update) {
const {User: {Posts}} = prev
const {subscriptionData: {data: {Post}}} = update
const postIndex = newPosts.findIndex(p => p.id === Post.id)
if (postIndex < 0) return prev
return {
...prev,
User: {
...prev.User,
Posts: [
...Posts.slice(0, postIndex),
Post,
...Posts.slice(postIndex + 1),
],
}
}
}
Unsubscribing and resubscribing when variables could be automated
Currently all the work of unsubscribing and resubscribing if variables change is pushed onto the developer. It wouldn’t be too hard to make my own component to do this automatically in a similar way to how Apollo query HOCs automatically refetch when variables change. But it definitely seems to me like something that should live in Apollo itself.
Issue Analytics
- State:
- Created 6 years ago
- Reactions:11
- Comments:10 (6 by maintainers)
Top GitHub Comments
Thanks for the great discussion @jedwards1211 @TheMickeyMike. A new design for the subscription API is being worked on, as part of our Apollo Client 3.0 work. I’ll close this for now (since we’re trying to make sure issues here are for bugs only), but your input is greatly appreciated. Thanks!
Well for now I’m using
name
for better understanding how Apollo cache works. Moreover, I need to check case when my client subscribe many subscriptions (ex. likeSub, dislikeSub, newPostSub…) and if there will be one open WebSocket pipe to my Apollo server, or each subscription opens new one, if so i will stick with one connection and probably doing some magic on server side with dynamic__typename
or add enum field to recognize action type and parse subscription data based on this actionType field, idk for know, just overthinking some basic stuff 🤔 Btw. this Blog is still in development, because I started my React/Node.js/Typescript journey couple days ago, even JS is new for me 😄 Now I know, that learning GraphQL in totally new technologies is really challenging 🤦♂️