Apollo-ios: Support for other WS protocols (ActionCable, Phoenix Channels, etc)

Created on 15 Jul 2019  路  11Comments  路  Source: apollographql/apollo-ios

As per Contributing Guidelines, just opening up a forum to discuss the solution before starting work on the change.

Backends like Rails and Phoenix don't easily allow the changing of protocols or a way to rewrite incoming WS messages AFAIK so client side handling seems to be the way to go.
Now that the concept of preflight rewriting is in this project, we could use the same principle with Subscriptions to make the sent/received messages conform to whatever protocol is used.

So in the case of ActionCable (example here) it'd need to wrap the initial subscription call in an AC specific data structure before sending it and conversely unwrap the received data to be processed as normal.

The proposed solution is to expose an optional delegate in the WebSocket transport that will be called before any WebSocket message is written similar to how the new preflight delegate it done.
On the other side it'll also call the delegate when a message is received to handle/unwrap the response before hitting the actual GraphQL logic.

This is unlikely to be a breaking change as the delegate is optional.

apollo-websockets enhancement

Most helpful comment

@Kaakati another solution (and the one I went with) was to ditch WebSockets entirely and just use silent push so it's a bit more streamlined. So the server will send a type and an id then the client will make a request using graphql.

It adds a bit of overhead but it just means I don't have to maintain two different systems and deal with manually serialising data since it uses graphql anyway. Also if you have an Android version, the solution can be reused instead of dealing with another round of WS headaches.

All 11 comments

This seems like an interesting idea, but I gotta admit, I'm not super familiar with these additional protocols. I have a few questions:

  • What are the advantages of using these additional protocols?
  • Are any of these used on top of the Apollo Server stack directly, or are they more generally used across GraphQL clients? (This isn't necessarily a dealbreaker, just trying to figure out how deep a hole we might be digging ourselves here in terms of support)
  • Does the response still wind up the same as other web socket responses? Otherwise I'd be concerned the parsing would break.
  • How would you test this?
  • More necessity than advantage since certain websocket backends only use their own protocol (with no option to customise it) so it makes it difficult to have conformance across different ecosystems. I imagine the reason being is since Rails/Phoenix/etc also deal with frontend they just provide a JS abstraction and leave native implementation up to whoever.
  • Any GraphQL client that communicates with a non JS backend and/or isn鈥檛 JS based will have this same issue. Might be talking out my ass here but if the server is a node server then this isn鈥檛 even an issue, so realistically the case I鈥檓 trying to solve is a bit of an edge case but still something to support.
  • That鈥檚 what I hope to achieve, think of this as a middleware in a sense where it鈥檚 optionally just serialising/deserialising the messages into something that either can understand. I鈥檝e gotten client -> server to work in my little POC and now working on the reverse.
  • I鈥檝e been thinking about this all day, but frankly since this really is just a delegate with some inout params, shouldn鈥檛 be too hard.

I鈥檒l push a PR sometime this week which should hopefully make more sense and clear up any questions. In essence, we鈥檙e just exposing a bit more of the websocket network transport flow similar to how the http network transport has its preflight delegate.

OK cool - I'll keep an eye out for your PR!

Bit of a hurdle, the current implementation relies on the server sending back an ID which refers to a subscriber on the client side. If the server doesn't, it just errors out, and most non apollo servers don't do this anyway.
I tried implementing this mechanism on the server side but the point of this client change is to reduce friction and the server changes was getting complex as is.

I've been thinking this over for the past few days, looks like there's 2 potential solutions:

  • Delegate method/flag to override ID checks and broadcast to all subscribers after parsing
  • Use multiple WebSocketClients instead of using just one and splitting its data

Any input will be appreciated, I'm learning towards the second option as the first might get a bit dicey with error handling.

I agree the first option sounds like it could be a serious tangle in terms of error handling.

You're probably still going to need to offer an option to get rid of the ID checks though - I'd say a flag is probably the better option - you'll know when you create the client whether you're going to want to check for IDs or not, and that almost certainly won't change through the lifecycle of the client. If it's known at creation time and never changes, that's a much better case for a flag parameter on the initializer rather than a delegate, IMO

Yeah nah I honestly don't think this is feasible at all, multiple WebSocket clients just straight didn't work and I can't think of any other way to get server -> client working. I have a feeling that it's related to https://github.com/daltoniam/Starscream/issues/551 which has a fix but is yet to be merged

I'm tempted to go down the first route with a flag and let the subscriptions figure out what they need, but that just seems a bit sketch.

As a last resort I'll try this with a WebSocket framework that I'm fairly sure works with multiple connections and see how it goes, but obviously this will expand the scope of this PR quite a lot.
If that fails, just gonna close this off and just write some documentation on this so it's clear as to what's supported and what's not.

Yeah any info you can share there would be helpful - the WebSocket stuff was contributed by the community and I'm still honestly wrapping my head around it.

I think this is just a pipe dream tbh, for anyone in the future looking for a solution the best possible alternative is to use silent push to trigger a query/mutation and this is probably preferred from a battery life point too.
Not sure how much I can document since I haven鈥檛 even got the existing subscription logic to work lol.

Going to close this and I鈥檇 probably recommend referring people here for any subscription related questions that involve anything that strays from the happy path.

OK, thanks very much for sharing!

@designatednerd I came across this because I'm looking for a solution for Subscriptions on rails, I've been trying to work around it, but the only feasible solution for me is to use one of the Swift ActionCable libraries with Apollo, I just hope I don't get conflict in Starscream library.

The React version already found their way around it, I hope soon we can get something on rails.

@Kaakati another solution (and the one I went with) was to ditch WebSockets entirely and just use silent push so it's a bit more streamlined. So the server will send a type and an id then the client will make a request using graphql.

It adds a bit of overhead but it just means I don't have to maintain two different systems and deal with manually serialising data since it uses graphql anyway. Also if you have an Android version, the solution can be reused instead of dealing with another round of WS headaches.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

designatednerd picture designatednerd  路  3Comments

hiteshborse12 picture hiteshborse12  路  4Comments

AnthonyMDev picture AnthonyMDev  路  4Comments

wnagrodzki picture wnagrodzki  路  4Comments

jeromeDms picture jeromeDms  路  5Comments