-
Notifications
You must be signed in to change notification settings - Fork 2.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
New GraphQLWsLink for graphql-ws subscriptions library #9369
Conversation
5349c38
to
7c6bc13
Compare
76dc22a
to
3f32357
Compare
(fixed accidental prettier applications) |
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.
Thanks for doing this @glasser. Some thoughts, but nothing blocking.
src/link/subscriptions/index.ts
Outdated
export class GraphQLWsLink extends ApolloLink { | ||
private client: Client; | ||
|
||
constructor(optionsOrClient: ClientOptions | Client) { |
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.
There is very little benefit to this sort of polymorphism, except maybe consistency and users not having to import graphql-ws
directly. However, I think the latter pro can also be considered a a con, as people are likely to improperly add graphql-ws
to their dependencies, or improperly configure the client and then open up an issue with us. Also, the type-sniffing of a dependency creeps me out because this would break if graphql-ws
ever added a subscribe
property directly to the client object. Therefore, I think it might be better to BYOWSC (bring your own websocket client).
But it’s not that big a deal.
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.
That's my general philosophy as well. I was echoing the existing link here, but very happy to just require you to use graphql-ws directly. It simplifies the doc too. @benjamn Any thoughts or shall I make this change?
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.
Since Client
is just an interface
in the graphql-ws
library, I agree it's important to preserve the ability to provide your own Client
, and we should not assume createClient
will always be the source of the implementation. In other words, I agree with @brainkim that the constructor
should take a Client
and not ClientOptions
(so it doesn't have to do any shape sniffing).
I'm sympathetic to the argument that new GraphQLWsLink({ url })
seems more ergonomic in the common case than new GraphQLWsLink(createClient({ url }))
, but the first version forces us to call (and bundle!) createClient
(a rather large function—333 lines) behind the scenes, much like new ApolloClient({ resolvers })
forces us to use/bundle our old LocalState
implementation, whereas something more verbose like
new ApolloClient({
localState: new LocalState({ resolvers }),
})
would allow developers to completely omit our LocalState
implementation, or provide their own. In other words, the simplicity of new GraphQLWsLink({ url })
has some large hidden costs.
With all that said, I believe we could safely re-export createClient
from this module, without impacting bundle sizes when it's not used, just so folks don't have to import it from graphql-ws
directly if they don't want 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.
OK, I think I want to move towards just taking the client (not creation options) and letting you import it yourself from graphql-ws
rather than re-exporting it. I don't think importing a function from a package you're required to install yourself anyway (optional peer dep) is that big a deal.
At this point, the only non-test dependency on graphql-ws
will be for the Client
type (which is an exposed part of the GraphQLWsLink API). Should we still have this as an optional peer dep? Or is there a simpler alternative? It would be nice if there was a mechanism in npm/ts for "type-only dep" but I don't think there really is one (devDependencies aren't appropriate for types that are actually used in the depending package's API, I think). I guess we could also just copy the Client interface into the file or something but that's not great either.
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.
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 the optional peer dep is still the way to go. Better to reuse the original Client
type than copy it over.
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.
Looking good, with a few questions/comments/responses. Thanks especially for all this documentation @glasser!
src/link/subscriptions/index.ts
Outdated
export class GraphQLWsLink extends ApolloLink { | ||
private client: Client; | ||
|
||
constructor(optionsOrClient: ClientOptions | Client) { |
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.
Since Client
is just an interface
in the graphql-ws
library, I agree it's important to preserve the ability to provide your own Client
, and we should not assume createClient
will always be the source of the implementation. In other words, I agree with @brainkim that the constructor
should take a Client
and not ClientOptions
(so it doesn't have to do any shape sniffing).
I'm sympathetic to the argument that new GraphQLWsLink({ url })
seems more ergonomic in the common case than new GraphQLWsLink(createClient({ url }))
, but the first version forces us to call (and bundle!) createClient
(a rather large function—333 lines) behind the scenes, much like new ApolloClient({ resolvers })
forces us to use/bundle our old LocalState
implementation, whereas something more verbose like
new ApolloClient({
localState: new LocalState({ resolvers }),
})
would allow developers to completely omit our LocalState
implementation, or provide their own. In other words, the simplicity of new GraphQLWsLink({ url })
has some large hidden costs.
With all that said, I believe we could safely re-export createClient
from this module, without impacting bundle sizes when it's not used, just so folks don't have to import it from graphql-ws
directly if they don't want to.
85a3327
to
464c9c6
Compare
Apollo Client currently contains `WebSocketLink` in `@apollo/client/link/ws` which uses the `subscriptions-transport-ws` library. That library is no longer actively maintained, and there is an improved fork called `graphql-ws`. The two libraries use different protocols so a different client link is required for `graphql-ws`. (While the WebSocket protocol does allow for subprotocol negotiation, neither server implementation supports this in a practical way.) This PR adds a new link `GraphQLWsLink` in `@apollo/client/link/subscriptions`. Its constructor arguments are the same as the `createClient` function in `graphql-ws` (or it can take a `Client` object returned from that function), and you need to install the optional peer dep `graphql-ws` instead of `subscriptions-transport-ws`. Once you've created the link, it works exactly like the old `WebSocketLink`. This PR changes the main subscriptions doc page to mostly document the new link, with an extra section at the bottom for the old link. The core GraphQLWsLink code is based on MIT-licensed code from the README of the graphql-ws repository. Fixes #8345. Part of apollographql/apollo-server#6058
Co-authored-by: Ben Newman <[email protected]>
Co-authored-by: Ben Newman <[email protected]>
b597925
to
11c0051
Compare
@benjamn Rebased and retargeted to release-3.6, with CHANGELOG added. |
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.
Thank you for bearing with my nits!
Would it be possible to release a new beta with this? |
New GraphQLWsLink for graphql-ws subscriptions library
New `GraphQLWsLink` and `@apollo/client/link/subscriptions` sub-package entry point for `graphql-ws` subscriptions library.
Apollo Client currently contains
WebSocketLink
in@apollo/client/link/ws
which uses thesubscriptions-transport-ws
library. That library is no longer actively maintained, and there is an
improved fork called
graphql-ws
.The two libraries use different protocols so a different client link is
required for
graphql-ws
. (While the WebSocket protocol does allow forsubprotocol negotiation, neither server implementation supports this in
a practical way.)
This PR adds a new link
GraphQLWsLink
in@apollo/client/link/subscriptions
. Its constructor arguments are thesame as the
createClient
function ingraphql-ws
(or it can take aClient
object returned from that function), and you need to installthe optional peer dep
graphql-ws
instead ofsubscriptions-transport-ws
. Once you've created the link, it worksexactly like the old
WebSocketLink
.This PR changes the main subscriptions doc page to mostly document the
new link, with an extra section at the bottom for the old link.
The core GraphQLWsLink code is based on MIT-licensed code from the
README of the graphql-ws repository.
Fixes #8345.
Part of apollographql/apollo-server#6058