-
Notifications
You must be signed in to change notification settings - Fork 337
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
[Proposal] X-GraphQL-Event-Stream header #120
base: master
Are you sure you want to change the base?
Conversation
PPS: apologies that this PR came through before I'd written a description, I accidentally did Cmd-Enter 😳 |
introspectionQuery, | ||
isType, | ||
GraphQLObjectType, | ||
} from 'graphql' |
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.
These need pruning
} | ||
] | ||
]; | ||
} |
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.
I now guarantee that every tab has a uuid; and move to referring to tabs by UUID to prevent race conditions when tabs open/close.
|
||
this.state.tabs.forEach(t => { | ||
this.updateSchema(t.uuid); | ||
}); |
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.
When we deserialise state we don't have any of the schemas; so go off and fetch them for each tab.
const oldHash = hash(currentTabPreviousState); | ||
const newHash = hash(currentTab); | ||
if (oldHash !== newHash) { | ||
this.debouncedUpdateSchema(currentTab.uuid); |
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.
Debounced so that typing into the URL bar doesn't queue up tens of network requests and cause lag.
window.localStorage.setItem('tabs', JSON.stringify(this.state.tabs.map(t => { | ||
const { uuid, endpoint, method, headers, name } = t; | ||
return { uuid, endpoint, method, headers, name }; | ||
}))); |
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.
Prunes things that we don't want to store to localStorage (namely: the GraphQL schema itself, and the streamUrl which we'll automatically refetch along with the GraphQL schema)
}); | ||
} | ||
return this._debouncedUpdateSchema[tabUUID](); | ||
} |
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.
- Need to clean this up on componentDidUpdate to prevent memory leaks
@@ -354,43 +624,46 @@ export default class App extends React.Component { | |||
</div> | |||
</form> | |||
<div className="graphiql-wrapper"> | |||
{ | |||
// THIS IS THE GROSSEST THING I'VE EVER DONE AND I HATE IT. FIXME ASAP | |||
} |
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.
Yeah, you thought yours was gross... We now hack into the internal state of GraphiQL... 😳 🤢
</div> | ||
</div> | ||
: null | ||
} |
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.
I'm doing some demo videos and want to reduce unnecessary clutter; please ignore this from this PR - we should probably add an option to hide tabs when there's only one.
This PR is to open a dialog; I'm not expecting it to be merged without some significant changes as I've kind of gone at the source with a hatchet... 😇
When a GraphQL server updates the GraphQL schema it would be nice if GraphiQL automatically reflected this. Over at PostGraphile we implement this with a
text/event-stream
from the server, which fires achange
event when the schema changes. Event streams are perfect for this because they're server-sent events, you don't need to worry about receiving data from the client (because you can't) and they are much simpler and more widely supported than websockets.I'm proposing that we make this a standard - if a GraphQL server supports updating its GraphQL schema it should create the header
X-GraphQL-Event-Stream
in response to every GraphQL request, passing as the value the relative or absolute URL to thetext/event-stream
event stream.The client can then subscribe to the event stream (which can be consumed with
EventSource
), and will re-inspect the schema whenever achange
event is received.An example of the event-stream implementation in PostGraphile is here:
https://github.com/graphile/postgraphile/blob/master/src/postgraphile/http/setupServerSentEvents.js
https://github.com/graphile/postgraphile/blob/5fd2cf218654e03021ef06ffe8f7556a874906b4/src/postgraphile/http/createPostGraphileHttpRequestHandler.js#L286-L295
I've implemented this in GraphiQL.app in this PR. Though doing so doesn't require much code I went a bit further and made sure that the GraphiQL navigation stack is persisted and fixed some other issues also... At the time I wasn't thinking about sending a PR so it kind of snowballed.
If you're interested in any of this I'm happy to send the changes as separate PRs that can be reviewed individually. I understand reviewing this PR as-is is a lot of work!
PS: a lot of the code in here is MIT licensed by @calebmer; I've modified it to make it work in GraphiQL.app.
🙏 Thanks!