graphql-playground icon indicating copy to clipboard operation
graphql-playground copied to clipboard

Proposal: support X-GraphQL-Event-Stream header

Open benjie opened this issue 7 years ago • 9 comments

Server-sent events are a perfect mechanism for informing a GraphQL client, such as graphql-playground, that the schema has been updated. They don't require the complexity of websockets, and they are uni-directional.

I'm proposing that when introspecting a GraphQL API, if the header X-GraphQL-Event-Stream is detected then GraphQL Playground should subscribe to the text/event-stream at that (relative or absolute) URL and when it receives the change event it should automatically re-introspect the GraphQL schema.

Not much code should be required to implement this, just something like:

const streamUrl = response.headers["x-graphql-event-stream"];
if (streamUrl) {
  const endpointUrl = new URL(endpoint);
  const streamUrl = new URL(streamUrl, endpointUrl);
  if (endpointUrl.host !== streamUrl.host) {
    throw new Error(
      `Stream and endpoint hosts don't match - '${streamUrl.host}' !== '${endpointUrl.host}'`
    );
  }
  const eventSource = new EventSource(streamUrl);

  eventSource.addEventListener("change", this.refreshSchema, false);
  eventSource.addEventListener("open", () => { /* ... */ }, false);
  eventSource.addEventListener("error", () => { /* ... */ }, false);
}

Would love to hear your thoughts.

benjie avatar Mar 29 '18 13:03 benjie

This sounds good! That would also be a pretty simple first task. Are you already using the X-GraphQL-Event-Stream header somewhere else or would we introduce it for this purpose?

timsuchanek avatar Apr 01 '18 08:04 timsuchanek

I’ve added it to PostGraphile and have an open PR with GraphiQL.app; happy to open a PR here too but it might take me a while to get around to. The code above is a tidied version of the GraphiQL.app PR.

benjie avatar Apr 01 '18 09:04 benjie

I really like this idea and wonder whether this could also be implemented with graphql-yoga out of the box. There is currently an open PR for hot-reload support: https://github.com/graphcool/graphql-yoga/pull/190

@benjie do you have any ideas how the Playground could pick up whether the underlying GraphQL server has been restarted?

schickling avatar Apr 02 '18 11:04 schickling

Would detecting the event-stream closing be sufficient?

benjie avatar Apr 02 '18 11:04 benjie

That sounds like a reasonable solution. So once it's closed it should start pinging the endpoint again like this:

image

schickling avatar Apr 02 '18 11:04 schickling

Sounds reasonable. I wrote an exponential backoff with a 30 second max cutoff (basically untested though) for GraphiQL.app:

https://github.com/benjie/graphiql-app/blob/44cfa3a52c1e547b858fcfa655ed1bee3fa7fab8/app/components/App.js#L262-L313

I was only hacking it together for my own purposes at the time; that code needs some serious refactoring.

benjie avatar Apr 02 '18 11:04 benjie

@benjie I'm working on a SSE Link for Apollo, it uses a single SSE channel for all subscriptions, using a secret token to identify the return channel without cookies. This is the first time I come across your efforts - did anything come of it?

My status: it's working in the ideal state but needs work on re-establishing subscriptions after connection loss or server restart, that kind of thing. Also, I'm trying to get it working with the Playground but hitting #1362

wmertens avatar Jan 19 '22 21:01 wmertens

@wmertens This was intended to be a way to indicate schema changes (for hot reloading a GraphQL schema); I don't see an issue with using it also for subscriptions using a different event name though. As it is, it has been implemented in Altair (https://sirmuel.design/a-better-graphql-developer-experience-with-x-graphql-event-stream-1256aef96f24) but I've not tracked its adoption into the rest of the ecosystem. A quick search on sourcegraph.com suggests it's used in PostGraphile, @enisdenjo's graphql-sse, and Altair but no other hits.

benjie avatar Jan 20 '22 09:01 benjie

graphql-sse does not depend on the X-GraphQL-Event-Stream header in any way. But it could be used for discovery indicating to the client that there's a SSE endpoint available.

Following my brief investigations, trying to integrate graphql-sse in PostGraphile, running both the schema update stream and GraphQL over Server-Sent Events protocol on the same route is no problem at all. In PostGraphile's case, a GET text/event-stream request without any query parameters is considered a schema update subscription; while on the other hand, the SSE protocol needs the GraphQL execution parameters - either in the query params for GETs or in the body for POSTs.

enisdenjo avatar Jan 20 '22 09:01 enisdenjo