https://kotlinlang.org logo
#apollo-kotlin
Title
# apollo-kotlin
s

Sean Proctor

02/09/2023, 11:40 AM
I'm trying to get Apollo working on a JS target. The same code on Android/JVM works fine, on JS it throws an exception "SubscriptionOperationException: Operation error <subscriptionName>". Is there anything JS-specific with subscriptions that I should know?
b

bod

02/09/2023, 11:44 AM
No, subscriptions should work the same on JS. Are you able to see the network traffic to see if anything doesn't look right? Or any other info in the logs?
s

Sean Proctor

02/09/2023, 11:48 AM
The logs are pretty useless. I'll check network traffic and see if there's anything interesting.
Copy code
Operation error syncAccount SubscriptionOperationException: Operation error syncAccount
    at WebSocketNetworkTransport$execute$o$collect$slambda_1.doResume_5yljmg_k$ (webpack-internal:///./kotlin/apollo-kotlin-apollo-runtime.js:4950:23)
    at WebSocketNetworkTransport$execute$o$collect$slambda_1.invoke_kvy33q_k$ (webpack-internal:///./kotlin/apollo-kotlin-apollo-runtime.js:4915:16)
    at sam$kotlinx_coroutines_flow_FlowCollector$0_3.l [as function_1] (webpack-internal:///./kotlin/apollo-kotlin-apollo-runtime.js:5003:16)
    at sam$kotlinx_coroutines_flow_FlowCollector$0_3.emit_1fbrsb_k$ (webpack-internal:///./kotlin/apollo-kotlin-apollo-runtime.js:5357:17)
    at SafeCollector.emit_1fbrsb_k$ (webpack-internal:///./kotlin/kotlinx.coroutines-kotlinx-coroutines-core-js-ir.js:20332:29)
    at WebSocketNetworkTransport$execute$slambda_1.doResume_5yljmg_k$ (webpack-internal:///./kotlin/apollo-kotlin-apollo-runtime.js:5780:63)
    at WebSocketNetworkTransport$execute$slambda_1.invoke_jwaue4_k$ (webpack-internal:///./kotlin/apollo-kotlin-apollo-runtime.js:5740:16)
    at _no_name_provided__qut3iv.l [as $transform_1] (webpack-internal:///./kotlin/apollo-kotlin-apollo-runtime.js:5822:16)
    at $emitCOROUTINE$1.doResume_5yljmg_k$ (webpack-internal:///./kotlin/apollo-kotlin-apollo-runtime.js:2394:51)
    at _no_name_provided__qut3iv.emit_1fbrsb_k$ (webpack-internal:///./kotlin/apollo-kotlin-apollo-runtime.js:2439:16)
m

mbonnin

02/09/2023, 11:52 AM
Got to love the coroutines stacktraces 😬 ....
If I read the exception right, this is a SubscriptionOperationException which means your backend has returned an error so it should be seen in the Browser devtools
(I think devtools show websocket data) If you're doing node I have no idea
Might be a case of a missing header in the initial handshake or something like this?
s

Sean Proctor

02/09/2023, 12:09 PM
Looking at the network over the wire was useless. looking in devtools, I only see a ws connection to ws://localhost:8080/ws I have no idea where that could come from. If that's what apollo is using, then I have a clear answer as what's gone wrong. Is there an easy way to get some debug messages from apollo?
b

bod

02/09/2023, 12:18 PM
hmm no there are no other logging levels than that
s

Sean Proctor

02/09/2023, 12:18 PM
yeah, that was something else. looking at the WS request in devtools doesn't tell me much.
b

bod

02/09/2023, 12:19 PM
ah! But you can see the messages sent/received?
s

Sean Proctor

02/09/2023, 12:20 PM
I can see that that the server responded:
Copy code
HTTP/1.1 101 WebSocket Protocol Handshake
Date: Thu, 09 Feb 2023 12:15:45 GMT
Connection: upgrade
Upgrade: websocket
Sec-WebSocket-Accept: CJgK75ri8M62LH3xkkdXjZpCWgk=
Sec-WebSocket-Protocol: graphql-ws
m

mbonnin

02/09/2023, 12:21 PM
Are you using Chrome? I'm almost sure I was able to see the different messages on the websocket after that initial handshake
s

Sean Proctor

02/09/2023, 12:22 PM
Yes
m

mbonnin

02/09/2023, 12:22 PM
Alright, maybe I dreamt it
Can you catch the exception? If the server responded something it should be in
exception.payload
s

Sean Proctor

02/09/2023, 12:35 PM
Ah ha! Progress. It looks like it's not authenticating the same with JS.
The header isn't being added to the WS request.
My client initialization looks like this:
Copy code
ApolloClient.Builder()
        .httpServerUrl(httpServeUrl)
        .addHttpInterceptor(AuthorizationInterceptor(token))
        .subscriptionNetworkTransport(
            WebSocketNetworkTransport.Builder()
                .serverUrl(webSocketServeUrl)
                .addHeader("Authorization", "Bearer ${token.value}")
                .build()
        )
        .build()
Works fine on Android. I guess I can only blame myself here.
m

mbonnin

02/09/2023, 1:00 PM
mmmm If you're calling
addHeader()
on JS, it should work similarly
Or is the client created in a different place on JS?
m

mbonnin

02/09/2023, 1:03 PM
Mmmm that feels wrong
Looks like there's some background stories to these lines 👀
We should throw early at least
s

Sean Proctor

02/09/2023, 1:09 PM
I'm trying to figure out how hasura expects me to authenticate in this case. If I remember well, they have a bit of a weird implementation.
Thanks so much for your help! Chased down the issue. Apparently Hasura accepts the authorization in either place, so this works on all platforms:
Copy code
ApolloClient.Builder()
        .httpServerUrl(httpServeUrl)
        .addHttpInterceptor(AuthorizationInterceptor(token))
        .webSocketServerUrl(webSocketServeUrl)
        .wsProtocol(
            SubscriptionWsProtocol.Factory(
                connectionPayload = {
                    mapOf("headers" to mapOf("Authorization" to "Bearer ${token.value}"))
                }
            )
        )
        .build()
m

mbonnin

02/09/2023, 1:25 PM
Thanks for the follow up. Definitely something to be aware of!