graphql-zeus
graphql-zeus copied to clipboard
Subscription header values from the headers getter are converted to strings which produces unexpected payloads
I am using Hasura as a subscription server. Now that you support the graphql-ws protocol after #301 🥳 I can almost completly drop apollo as a dependency of my project. However there one small thing blocking my connections from working.
It seems like for some reason the Zeus client is converting the nested fields of a payload to strings. Let me illustrate what I mean with an example.
To connect to Hasura the websocket client has to send this data:
{"type":"connection_init","payload":{"headers":{"x-hasura-admin-secret":"xxxxxxx"}}}
Notice that the payload has a headers field which itself is an object of actual headers key value pairs.
I am currently doing this with apollo something like this:
const wsLink = new WebSocketLink({
uri: "wss://" + graphql_uri,
options: {
lazy: true,
connectionParams: { headers: { "x-hasura-admin-secret": admin_secret } }
reconnect: true
}
});
This works as expected generating the required payload. However with Zeus using this code:
import {admin_secret, graphql_url_wss} from "@/config"
const wsChain = Subscription(graphql_uri_wss, {
get headers() {
// should turn into {"type":"connection_init","payload":{"headers":{"x-hasura-admin-secret":"xxxxxxxxxxx"}}} but doesn't
return { headers: { "x-hasura-admin-secret": admin_secret } };
},
});
I get this: {"type":"connection_init","payload":{"headers":"[object Object]"}}
and ofcourse Hasura does not except this. I have tried work arounds like this:
get headers() {
return { headers: `{ "x-hasura-admin-secret": "${admin_secret}" }` };
}
which gives: {"type":"connection_init","payload":{"headers":"{ \"x-hasura-admin-secret\": \"xxxxxxxx\" }"}}
So that just turns the whole value of headers into an escaped string and sadly Hasura also does not accept this.
When I try another potential work around (converting the whole headers return value into a string):
get headers() {
return JSON.stringify({ headers: { "x-hasura-admin-secret": ${admin_secret} }` });
}
Then I get this error:
Uncaught TypeError: Headers constructor: Argument 1 could not be converted to any of: sequence<sequence<ByteString>>, record<ByteString, ByteString>.
apiSubscription index.ts:13
Subscription index.ts:248
<anonymous> subscriptions.ts:8
So it seems that Zeus is converting the values of the keys in the payload to strings. This currently still makes the Zeus subscriptions incompetable with Hasura. I hope this is an easy fix and that the converting to string is not done for any important reason and that it can just be removed or at least turned off somehow.
But perhaps I am missing some way to get the payload to turn out how I want without converting the values to a string. Is there some other method like headers which is not documented? Perhaps the way to solve this issue without making a breaking change is to add another getter called payload
which is just added with ...
to the payload object without any other further conversions.
Hey!
Does this work for you?
const wsChain = Subscription(graphql_uri_wss, {
get headers() {
return { "x-hasura-admin-secret": admin_secret };
},
});
Hey!
Sadly that does not work. This header getter generates this:
{"type":"connection_init","payload":{"x-hasura-admin-secret":"xxxxx"}}
And then hasura returns: Connection initialization failed: x-hasura-admin-secret/x-hasura-access-key required, but not found
This is because x-hasura-secret really has to be within a headers object in the payload. Now there is no headers field in the payload so the x-hasura-admin-secret is not found.
Ok the problem is probably the normalization that happens here: https://github.com/graphql-editor/graphql-zeus/blob/master/src/TreeToTS/functions/apiSubscription/graphql-ws.ts#L7
Can you try patching your client with the following code?
export const apiSubscription = (options: chainOptions) => {
const client = createClient({
url: String(options[0]),
connectionParams: options[1]?.headers ?? {},
});
// ...
Yes this worked. I replaced it in the generated index.ts and now it connects correctly.
So is this going to be changed? Because I don't want to manually patch the client everytime my graphql schema changes.
Can I help to implement this?