-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
[RFC] GraphQL Subscriptions #267
Conversation
rfcs/Subscriptions.md
Outdated
|
||
**Possible Solutions** | ||
|
||
We broadly categorize realtime API solutions into three types: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if is a typo, but there are six types defined below (instead of the three mentioned here).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(If you look at the rendered view, it's a nested list)
This is really great! It was awesome that this took into account feedback from a variety of sources. I think this sketch of the subscriptions lifecycle is compatible with the one currently implemented in our We've designed it in such a way that anyone currently using GraphQL.js can just drop it in and start using subscriptions right away, and we have some adapters for Redis, MQTT, etc. We'll make sure that implementation is always up to date as new conclusions are reached in the discussion. After all, this RFC is just the beginning, not something set in stone. There's also a transport protocol over websockets (something that shouldn't be covered in a spec, but you need a transport to use it) which we are currently using in production. It isn't coupled to any particular client or web server package, and implements all of the important lifecycle events necessary: https://github.com/apollographql/subscriptions-transport-ws I encourage people interested in this proposal to try out those packages with their existing server and experiment with GraphQL subscriptions today! I think that's going to be a great way to collect more feedback about this design. |
Awesome! Here's a rough schedule of what happens next: This is a really well written RFC, but this should not deter anyone from asking questions - both getting into the weeds and challenging core assumptions and aspects of the proposal. An RFC is the beginning of a discussion. Let's leave this PR open as a place for discussion for the time being. |
Really happy to see progress on subscriptions! Good job, it looks great so far! 🏆
It would be interesting to see the semantics of this mapping. In particular, do the root subscription fields have any special semantics or the interpretation of the root field semantics can be defined by a GraphQL server (for example, does every root field represents specific type of event like Also another remark. The RFC assumes and emphasizes that subscription communication is bi-directional. I wonder whether this restriction is necessary or required. I definitely see appeal of having bi-directional communication. For instance, it can multiplex several subscriptions in a single WebSocket connection and provide extended life-cycle phases for further optimizations. But in the most basic form, uni-directional communication (server → client) can be sufficient to implement semantics described in the RFC. A while back I implemented an example GraphQL subscription service that is based on SSE, which is uni-directional. As far as I can tell, it satisfies the semantics described in this RFC, but simplifies life-cycle a bit:
Would love to hear your thoughts on this aspect of the RFC :) Also regarding bi-directional communication. As far as I can tell, it defines a small protocol - something similar to apollo WS transport protocol. Does this mean that in future we will have additional specification, alongside of GraphQL, that will define and standardize this bi-directional communication protocol (life-cycle phases, data format, command types like |
@OlegIlyenko my interpretation is that "bidirectional" means that in your description there are arrows that go from client -> server and also server -> client. |
Aren't any client -> server legs just mutations? This looks consistent with what's implemented between relay-subscriptions and graphql-relay-subscription as well. A few comments:
|
We started our implementation without a "subscription active" notification, but later had to add it. Because the first result from your subscription could arrive a long time after the initial request, you don't know if you're actually receiving those updates. In some cases, you want to be able to look at the state of the subscription to decide if you should be polling for data or simply waiting for events to arrive, especially if you don't want to miss some crucial updates to the information you're looking for. Essentially, it can be very hard to know what happened if you subscribe and get back total silence - did the subscription somehow fail or stall, or did it successfully start and you should expect to get results in the future? @taion I think you're using socket.io right? Maybe they have a system that checks if the subscription is successful under the hood? |
To clarify, I'm saying that "subscription active" notifications aren't required in all cases. It really depends on the app. If it's something like chat, then "subscriptions are down" is a big deal and you want to show some user feedback. For something like, say, showing "new comment" prompts on a GitHub issue page, you don't really care. A general purpose subscription layer probably wants to expose something like those "subscription active" notifications to handle all uses cases, but they're not always necessary in more specialized cases. |
Good call - I agree that it is not necessary in cases where the subscription is not providing critical functionality. I think one other factor to consider is that returning an error about the initial subscription definitely is necessary - for example, if the query is not valid at all you want to receive some validation error in response to your initial subscription. Without a message that there was a successful subscription (essentially it just tells you the query validated etc) there's this time when you're not sure if there will be an initial error or not. |
Yup – error handling is necessary in general. I guess it depends how in-detail this spec wants to go on what the network layer should do. |
Questions like whether a subscription can do events singly or must be multiple, as asked in the blog post, point to the possbible usefulness of an abstraction like Observable, which is not inherently tied to multiplicity of responses. Also, that it is moving through standardization in the language would be helpful too. I'm also a fan of the DDP 'ready' event that announces the change from serving up existing old data to serving up new responses. While not strictly necessary, it lets you do things like remove loading graphics as you already know quite well @stubailo :) My 2c on unsubscribe is it should be a message as well (as in DDP), but obviously the server should do the right thing if a connection goes stale and no unsubscribe was received. PS I'm in the category of someone whose org may adopt GraphQL if it had subscriptions. Right now we're rolling something on Meteor DDP until we see if Apollo could give us a compatible-enough experience. |
Observable is really a JS-level implementation detail, no? I don't think it's pertinent at the level of this spec. |
Thanks for writing this up, I'm looking forward to seeing how it evolves! I had a couple questions:
|
I don't think in general subscription queries should return an immediate result. If I have a subscription on e.g. items being added, what would that result be? |
@taion - The point of it wasn't to prescribe an implementation, but to convey semantics by referring to an existing spec to broaden the discussion. The contract covers issues like you mentioned above such as whether you get a result upon subscribe. http://reactivex.io/documentation/contract.html |
Whatever the solution may be. I 👍 the addition to the spec, because without a subscription system, GraphQL can hardly be considered as "state of the art", when if fact, it really should be. Edit: Had to also link to Sashko's great blog post. Scott |
One thing that's not necessarily clear to me (and to be honest, I haven't been down in the trenches with subscriptions, unlike others commenting here) is this: When the client wants to create a subscription, is it just executing a GraphQL query with a special operation? Or is it sending a package of "something" (including a GraphQL query, variables, etc) to something else, and then that something else is interacting with GraphQL? I was previously under the impression that if I sent this query to my GraphQL server, using the exact same transport/etc mechanics as any other query, that it was creating a subscription: subscription {
someField {
# sub-selections
}
} In this scenario, the GraphQL library (graphql-ruby in my case) is handling the "book-keeping" of the subscription, and somehow I'm plugging in:
But after reading this spec, it sounds like I'm instead directing this to something other than my GraphQL library, handling the book-keeping myself, and periodically running a not-so-special query and sending the (black box) payload to the client. Essentially, what I'm getting at, is should we expect authors of GraphQL frameworks to be providing the book-keeping for subscriptions, etc and then exposing some fancy integration points (events and networking)? Or should we expect a new eco-system of subscription frameworks to grow up alongside existing GraphQL frameworks? |
I imagine implementation-wise there's not much actual change, right? As set up right now, GraphQL.js can execute a |
Yes, the idea is that you can add subscriptions on top of the existing GraphQL execution libraries by adding a subscription manager or subscription gateway. And that thing's job is to re-execute the subscription query against the GraphQL server in response to the event.
I think that's about right - in terms of a library like GraphQL.js I think the one thing that could be added is a way to map the subscriptions fields to events. I think there would be a lot of value in putting that right next to the resolver code. Oh, and one more thing - the resolver for a subscription field has one more parameter now, which is the "payload", so now that resolver takes in That could come in three different places:
I think I'm in favor of (3), which requires a change to GraphQL.js to have the execution function take an extra |
It'd be great if running a subscription could return some description of the relevant event set as a first-order concern, though the current workarounds aren't too bad. I'd prefer subscription payload come through as |
Hey all, amazing feedback and discussion so far, but it’s clear the github PR single-comment-thread feedback format is not suited for hosting multiple conversations at once. I’m going to go through the feedback and organize everything into open and closed topics. Closed topics are either obvious or have clear consensus and will be merged into the proposal in the next edit. Open topics are still being discussed (I will try to tag everyone who’s been involved/interested on each topic). Once I’ve organized this list, I’ll need everyone’s help:
After that, I’ll merge this PR (clearly marked as "WIP") and open issues for each of the open topics so that we can have a more focused conversation. As each open topic is resolved, we will make the appropriate edits as part of a new PR. My main goals are to make sure all the great conversations here are captured without competing for attention and that we can make incremental progress on getting this into the Spec. |
Somehow a query needs to be designated as "live" by the client. Or, as you said, and I think we are on the same wavelength on this, the queries that can be called as "live" need to be defined on the server in some way and be obvious/ findable through introspection. Scott |
Here's my attempt to organize this thread into topics: Closed
Open
Please let me know if I've missed your topic or if you would like to be added/removed from anything. I'll leave this post here for a bit so everyone tagged has a chance to review. After that, I'm going to merge this PR and file issues for all open topics. |
The use of SSE v WS is an application-level implementation detail. All that's needed is the server being able to push messages to the client somehow, which distinguishes this from request/response.
I'd prefer to have this state be stored outside of GraphQL.js. I want to e.g. manage my own Redis subscriptions backing my GraphQL subscriptions, and I feel like the control logic is more straightforward if I have something I can call when my subscription fires, rather than passing e.g. an observable into GraphQL.js.
This is a framework-level implementation question. I'd prefer GraphQL.js use the existing
I agree with this. It's the "natural" way to write the spec.
I think this is an application-level thing. "State" is not a concept that is inherent to GraphQL. Subscribing to e.g. streams of newly created items also doesn't necessarily admit an easy representation of current state. Imagine subscription updates that don't reflect idempotent operations – in such cases there isn't really a coherent initial state.
@stubailo's point is exactly right here. Live queries should be thought of separately from event-based subscriptions. They can be used to solve similar problems. However, scaling event-stream-based subscription systems is relatively well-understood; the same does not apply for live queries, and for anyone looking to ship something scalable now, live queries aren't really an option.
This is an application-level detail. If someone wants to subscribe to e.g. a "top X" stream but not re-process that stream, then there's nothing at the framework level that prevents e.g. the "trending tweets" subscription from using different events than the "new tweet" subscription. |
@taion Live queries certainly could be scalable, especially with GraphQL's emphasis on separating logic for each field. I'm looking at implementing a scheduler for Magellan that could do that. But, you're right. Subscriptions are a different topic from live queries. And IMO, live queries don't need a change to the spec to be implemented. Directives are server-specific with introspection, and that should be all you need. |
Just to clarify, when I say "live queries", I mean something like the For an example of a subscription-based API, see https://github.com/edvinerikson/relay-subscriptions/blob/v1.0.0/examples/todo/js/components/Todo.js#L124-L148. If this were using live queries, then perhaps it would just look like: fragment on Todo @live {
id
complete
text
# ...
} However, if you're doing various things such as using microservices and splitting up GraphQL objects across multiple backend services, &c., how to efficiently implement live queries can be much less than obvious, while event-based systems are much more straightforward. |
Since there's excitement and support for the direction of this RFC, I'm going to commit it as an official tracked RFC. @robzhu just opened up issues for all of the various topics still under discussion - so this is still very much an open design! |
So excited! |
Me too! 👍 Scott |
AHHHHHHHHHHHHH!!!!!!! |
* RFC: GraphQL Subscriptions * Fix cutoff content from last checkin * Fix diagram spacing * Fix diagram spacing for real * Minor wording fixes and higher-res images * More wording clarity fixes * Make overpushing/underpush wording consistent
Let's add GraphQL Subscriptions to the Spec!