graphql-over-http icon indicating copy to clipboard operation
graphql-over-http copied to clipboard

X-GraphQL-Event-Stream for being notified that the GraphQL schema has updated

Open benjie opened this issue 6 years ago • 7 comments

Server-sent events are a perfect mechanism for informing a GraphQL client 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 interested clients 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:

// refreshSchema is a function you provide which triggers re-introspection

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", refreshSchema, false);
  eventSource.addEventListener("open", () => { /* ... */ }, false);
  eventSource.addEventListener("error", () => { /* ... */ }, false);
}

(You may want to throttle the refreshSchema method.)

This has been baked into PostGraphile for a while; I opened an implementation PR to @skevy's GraphiQL.app a good while ago; and @imolorhe has implemented it independently in Altair. Samuel wrote more about it here: https://sirmuel.design/a-better-graphql-developer-experience-with-x-graphql-event-stream-1256aef96f24. We'll probably be implementing it in the new version of GraphiQL too, once we have the plugin system in place.

What are your thoughts?

benjie avatar Dec 17 '19 10:12 benjie

sometimes this feature can come in handy

sungam3r avatar Dec 17 '19 22:12 sungam3r

@benjie do you have a simple GraphQL server repo that has this implemented for demo purposes? I've been finding it hard to see this implementation actually working

imolorhe avatar Dec 22 '19 10:12 imolorhe

No; but it is implemented in a separate small file:

https://github.com/graphile/postgraphile/blob/0ad482d5f9c4e8314d86f0ffc9f12d9e320578b1/src/postgraphile/http/setupServerSentEvents.ts

(Some of this file handles the differences between Koa and Express/Connect, so you may be able to trim a fair bit of it.)

That file is called from a middleware with this code:

      // Setup an event stream so we can broadcast events to graphiql, etc.
      if (pathname === '/graphql/stream') {
        if (req.headers.accept !== 'text/event-stream') {
          res.statusCode = 405;
          res.end();
          return;
        }
        setupServerSentEvents(req, res, options);
        return;
      }

The other thing is we have an EventEmitter and when the GraphQL schema has changed we publish the schemas:changed event to it; but your implementation may do something different.

benjie avatar Jan 07 '20 10:01 benjie

This is a fascinating idea. I really like it. I can also see that it'd be nice to see a standard around this mechanism. I'm thinking it'd be best for this to be post-1.0 release. What do you think?

sjparsons avatar Jul 01 '20 16:07 sjparsons

Definitely post 1.0 is fine. It’d be optional anyhow.

benjie avatar Jul 04 '20 06:07 benjie

@enisdenjo what do you think of adopting this for graphql-http? would you be open to a PR that allows the user to opt into this for NODE_ENV=development or whatever they configure? or should it come as a separate middleware?

I would like to implement this for the editor tooling (LSP server and GraphiQL), finally!

acao avatar Mar 04 '24 02:03 acao

What exactly? Add the header? I don't think it's necessary to integrate this into the library as it can simply be added by the user itself.

enisdenjo avatar Mar 04 '24 08:03 enisdenjo