-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
feat: @defer, @stream, websockets support in a createGraphiQLFetcher utility #1770
Conversation
cc @n1ru4l @enisdenjo @robrichard @lilianammmatos @Urigo: you all might find this PR interesting as I'm using your all's code. I figured you might know of some important common configuration settings I might be overlooking for the first iteration? particularly here and here is where the logic in question resides. I think it's best to build a proper integration suite for this. |
e7244bd
to
031b56b
Compare
Codecov Report
@@ Coverage Diff @@
## main #1770 +/- ##
==========================================
- Coverage 65.95% 65.76% -0.20%
==========================================
Files 83 85 +2
Lines 4991 5088 +97
Branches 1581 1606 +25
==========================================
+ Hits 3292 3346 +54
- Misses 1672 1715 +43
Partials 27 27
Continue to review full report at Codecov.
|
031b56b
to
a1e733e
Compare
try { | ||
try { | ||
// TODO: defaults? | ||
wsClient = createClient({ |
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.
@enisdenjo so, this is the default pattern, if only subscriptionsUrl
is provided. the user can optionally provide their own instance of a client, which would bypass this behavior entirely.
the idea of a wsClientOptions
option came to mind, however a wsClient
option itself seemed even more powerful. do you think a wsClientOptions
with graphql-ws
's ClientOptions
should be offered as well, 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.
Other runtime options can be configured and I would recommend allowing the user to do so. You may have a custom server configuration and might need to fine tune the client so that they work in coherence.
But, this can be achieved by providing your own client instance, so, subscriptionsUrl
+ wsClient
would be just enough.
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.
Thinking about it a bit more, I'd expose the connectionParams
too. I see it being used a lot because it is the "headers" of the WS connection.
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.
ah, fantastic idea!
a1e733e
to
27081f1
Compare
try { | ||
try { | ||
// TODO: defaults? | ||
wsClient = createClient({ |
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.
Other runtime options can be configured and I would recommend allowing the user to do so. You may have a custom server configuration and might need to fine tune the client so that they work in coherence.
But, this can be achieved by providing your own client instance, so, subscriptionsUrl
+ wsClient
would be just enough.
}, | ||
"dependencies": { | ||
"graphql-ws": "^4.1.0", | ||
"graphql-transport-ws": "^1.9.0", |
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.
Why are you using the deprecated graphql-transport-ws
lib? Its just an old name for graphql-ws
, the two are fundamentally the same. graphql-ws@1.9.1
= graphql-transport-ws@1.9.0
I wouldnt advocate the usage of the deprecated library at all. The full focus should be on graphql-ws
.
"graphql-transport-ws": "^1.9.0", |
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.
we want to have backwards compatibility with the old transport spec since it's so widely used?
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.
Thats subscriptions-transport-ws
.
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.
is graphql-ws
backwards compatible with older client implementations?
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.
graphql-ws
implements the new Protocol exclusively. It is not interoperable with Apollo's Protocol.
A WebSocket can use just one subprotocol; therefore, built-in interoperability is simply not feasible. Read more about this topic here and here.
If you want to support both the deprecated Apollo's lib and the new one, you have to configure a connection delegator on the server side.
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.
Specifically this line of code ("graphql-transport-ws": "^1.9.0"
) should be removed, @enisdenjo authored both and renamed graphql-transport-ws
to simply graphql-ws
👍
On the broader subject of subscriptions-transport-ws
vs graphql-ws
support; I also agree with @enisdenjo - the best approach is to try connecting with graphql-ws
and if that connection fails then try again using subscriptions-transport-ws
. Even though probably 95% of GraphQL APIs that support websockets currently don't support graphql-ws
, in future this should shift since Apollo's protocol is officially unmaintained; we should skate to where the puck is going not where it has been. It's possible for servers to support both protocols (as the next release of PostGraphile does), in that case graphql-ws
should win.
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.
@benjie awesome! thats what we're doing in this PR already, just convincing @endisdenjo that it makes sense
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 think what I meant to add here is this:
https://www.npmjs.com/package/subscriptions-transport-ws
got graphql-transport-ws
and subscriptions-transport-ws
mixed up sorry!
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.
@enisdenjo my apologies for taking so long to realize this 😆 I knew something was wrong when I couldn't import the SubscriptionsClient
class I was used to!
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.
Haha, thats ok! Glad you figured it out. 😄
url, | ||
wsClient: createClient({ | ||
url: subscriptionsUrl, | ||
keepAlive: 2000, |
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 think that the keepAlive
is superfluous here. Its mainly useful in applications which have a lot of moving parts where you dont want to waste resources (a lot of connects/disonnects to a WS server).
I would, however, recommend using the lazy: false
option. This way GraphiQL would immediately connect to a server and the user can know right away if WS works (instead of being forced to issue a subscription operation just to check the basics).
keepAlive: 2000, |
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.
oh yeah i was just adding an arbitrary setting so people could see that more than just URL is available. lazy: false
sounds like a good idea, I can add that to the default implementation
/** | ||
* url for websocket subscription requests | ||
*/ | ||
subscriptionsUrl?: string; |
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.
The WebSocket transport supports all GraphQL operations, including streaming queries (like the @live
or @deferred
directive).
I guess WS would be used only for subscriptions, which is why the prop makes sense; but, ultimately you can communicate with GQL only through WS if you wish.
Just a thought.
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.
yes we're familiar with this pattern, however the intention of this library is to support the more widely adopted transport spec implementations, the "90%+ case" if you will.
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 someone wants to ship a fetcher for that pattern in a standalone package like this one, they are welcome to
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.
Would it be possible to create a simple option for using WebSocket for everything exclusively or an optional function configuration option that receives the operation payload and then depending on the returned value of that function (e.g. http
or ws
) the corresponding protocol will be used?
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.
folks are welcome to create their own standalone fetcher packages like these for other patterns! we are only interested in adopting widely used, advanced transport specs for this effort
"fork-ts-checker-webpack-plugin": "4.1.3", | ||
"graphql": "15.4.0", | ||
"graphql-ws": "^4.1.0", | ||
"graphql-transport-ws": "^1.9.0", |
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.
Same as above, graphql-transport-ws
is deprecated. I dont recommend using it!
"graphql-transport-ws": "^1.9.0", |
@enisdenjo another issue I had is that I couldn't get the |
If you're using In the meantime, you have to configure |
i already tried the recipe and it didn't work. i’m not referring to the graphiql implementation in |
Ahh, alright then. Please share the problem you faced and I will investigate. |
@enisdenjo i fixed it! the major delta from your recipie was for some reason that i needed to use the express i've been battling with |
A bunch more changes are coming! including an implementation in netlify |
I am also building a fetcher support for both graphql-ws and subscriptions-transport-ws on express-graphql's graphiql IDE, it looks like this fetcher can be shared and reused? Will a umd version being published? |
hey great question! this library will ship as |
That could be just fine 😄 express-graphql's graphiql IDE already has dependency on graphiql umd bundle. About this buildFetcher() method's signature , I can confirm passing |
good news! anyone following might want to try a field called |
e32555a
to
a774e93
Compare
a774e93
to
3876b5f
Compare
e66366b
to
e75a498
Compare
e75a498
to
26feeeb
Compare
May I suggest we wrap |
@maraisr i was just pondering that a little bit ago! i saw it linked in the spec and was gonna check it out tomorrow. would you like to create a PR against this PR to replace it? |
I think we're good here! Any necessary improvements can come in later iterations. |
…utility (#1770) - support for `@defer` and `@stream` in `GraphiQL` itself on fetcher execution and when handling stream payloads - introduce `@graphiql/toolkit` for types and utilities used to compose `GraphiQL` and other related libraries - introduce `@graphiql/create-fetcher` to accept simplified parameters to generate a `fetcher` that covers the most commonly used `graphql-over-http` transport spec proposals. using `meros` for multipart http, and `graphql-ws` for websockets subscriptions. - use `graphql` and `graphql-express` `experimental-defer-stream` branch in development until it's merged - add cypress e2e tests for `@stream` in different scenarios - add some unit tests for `createGraphiQLFetcher` - add `changesets` because lerna publish no longer works. get rid of commitlint
…ility (#1770) - support for `@defer` and `@stream` in `GraphiQL` itself on fetcher execution and when handling stream payloads - introduce `@graphiql/toolkit` for types and utilities used to compose `GraphiQL` and other related libraries - introduce `@graphiql/create-fetcher` to accept simplified parameters to generate a `fetcher` that covers the most commonly used `graphql-over-http` transport spec proposals. using `meros` for multipart http, and `graphql-ws` for websockets subscriptions. - use `graphql` and `graphql-express` `experimental-defer-stream` branch in development until it's merged - add cypress e2e tests for `@stream` in different scenarios - add some unit tests for `createGraphiQLFetcher` - add `changesets` because lerna publish no longer works. get rid of commitlint
…ility (#1770) - support for `@defer` and `@stream` in `GraphiQL` itself on fetcher execution and when handling stream payloads - introduce `@graphiql/toolkit` for types and utilities used to compose `GraphiQL` and other related libraries - introduce `@graphiql/create-fetcher` to accept simplified parameters to generate a `fetcher` that covers the most commonly used `graphql-over-http` transport spec proposals. using `meros` for multipart http, and `graphql-ws` for websockets subscriptions. - use `graphql` and `graphql-express` `experimental-defer-stream` branch in development until it's merged - add cypress e2e tests for `@stream` in different scenarios - add some unit tests for `createGraphiQLFetcher` - add `changesets` because lerna publish no longer works. get rid of commitlint
…ility (#1770) - support for `@defer` and `@stream` in `GraphiQL` itself on fetcher execution and when handling stream payloads - introduce `@graphiql/toolkit` for types and utilities used to compose `GraphiQL` and other related libraries - introduce `@graphiql/create-fetcher` to accept simplified parameters to generate a `fetcher` that covers the most commonly used `graphql-over-http` transport spec proposals. using `meros` for multipart http, and `graphql-ws` for websockets subscriptions. - use `graphql` and `graphql-express` `experimental-defer-stream` branch in development until it's merged - add cypress e2e tests for `@stream` in different scenarios - add some unit tests for `createGraphiQLFetcher` - add `changesets` because lerna publish no longer works. get rid of commitlint
…ility (#1770) - support for `@defer` and `@stream` in `GraphiQL` itself on fetcher execution and when handling stream payloads - introduce `@graphiql/toolkit` for types and utilities used to compose `GraphiQL` and other related libraries - introduce `@graphiql/create-fetcher` to accept simplified parameters to generate a `fetcher` that covers the most commonly used `graphql-over-http` transport spec proposals. using `meros` for multipart http, and `graphql-ws` for websockets subscriptions. - use `graphql` and `graphql-express` `experimental-defer-stream` branch in development until it's merged - add cypress e2e tests for `@stream` in different scenarios - add some unit tests for `createGraphiQLFetcher` - add `changesets` because lerna publish no longer works. get rid of commitlint
…ility (#1770) - support for `@defer` and `@stream` in `GraphiQL` itself on fetcher execution and when handling stream payloads - introduce `@graphiql/toolkit` for types and utilities used to compose `GraphiQL` and other related libraries - introduce `@graphiql/create-fetcher` to accept simplified parameters to generate a `fetcher` that covers the most commonly used `graphql-over-http` transport spec proposals. using `meros` for multipart http, and `graphql-ws` for websockets subscriptions. - use `graphql` and `graphql-express` `experimental-defer-stream` branch in development until it's merged - add cypress e2e tests for `@stream` in different scenarios - add some unit tests for `createGraphiQLFetcher` - add `changesets` because lerna publish no longer works. get rid of commitlint
…utility (graphql#1770) - support for `@defer` and `@stream` in `GraphiQL` itself on fetcher execution and when handling stream payloads - introduce `@graphiql/toolkit` for types and utilities used to compose `GraphiQL` and other related libraries - introduce `@graphiql/create-fetcher` to accept simplified parameters to generate a `fetcher` that covers the most commonly used `graphql-over-http` transport spec proposals. using `meros` for multipart http, and `graphql-ws` for websockets subscriptions. - use `graphql` and `graphql-express` `experimental-defer-stream` branch in development until it's merged - add cypress e2e tests for `@stream` in different scenarios - add some unit tests for `createGraphiQLFetcher`
This introduces a backwards compatible client wrapper that provides
graphql-ws
subscriptions specification, as well as support for@defer
and@stream
IncrementalDelivery
spec.It wraps clients such as
fetch-multipart-graphql
,graphql-ws
and even legacy support forsubscriptions-transport-ws
new npm packages:
@graphiql/toolkit
will become a set of types and utilities for building tools like GraphiQL. it will here prevent a minor cyclic dependency issue between the fetcher module andgraphiql
itself, which impacts typescript project references. breaking outgraphiql
into more component packages as such will enable other architectural changes for 2.0@graphiql/build-fetcher
is the standalone utility for generating aFetcher
instance that for this iteration will support http multipart IncrementalDelivery as well as graphql-ws transportimportant changes:
graphiql
Fetcher
prop now supports an array of incremental responses.Todo
graphql-ws
& multipart withexpress-graphql
defer stream release taghow to demo locally
from the repo root:
yarn --force && yarn build
yarn start-graphiql
then this query should be enough to get started:
demo in netlify?
https://deploy-preview-1770--graphiql-test.netlify.app/?query=%7B%0A%20%20isTest%0A%20%20stream%3A%20streamable(delay%3A%20500)%20%40stream(initialCount%3A%202)%20%7B%0A%20%20%20%20text%0A%20%20%7D%0A%20%20anotherStream%3A%20streamable(delay%3A%20200)%20%40stream(initialCount%3A%200)%20%7B%0A%20%20%20%20text%0A%20%20%7D%0A%7D%0A
feedback welcome!
I'm interested in feedback on all working features, including the new utility, and the GraphiQL
IncrementalDelivery
implementation. (anything except documentation, tests, still some polish needed there, haha!)Credit
@defer
&@stream
support has been a HUGE effort across the GraphQL community.Spec development and libraries by @robrichard @lilianammmatos and @enisdenjo
Prior Art from @Urigo and @n1ru4l