Skip to content

Discussion: subscriptions API design improvements #2892

@jedwards1211

Description

@jedwards1211

I'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 ids 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions