I have a NODE application that also acts as a GraphQL client. I need to fetch the data from a GraphQL server using SUBSCRIPTION requests.

The GraphQL server validates the incoming HTTP upgrade message during the handshake to establish the WebSocket connection. The validation is done by looking at the Cookie header for a token.

I am using the graphql-ws library. The library does allow me to create a subscription client, but I haven't been successful in adding the authorization headers to the outgoing HTTP upgrade messages.

Here is my code:

import { createClient } from 'graphql-ws';
    
const sClient = createClient({
        url: graphqlWsServer,
        webSocketImpl: WebSocket
      });

      (async () => {
        try {
            const subscription = sClient.iterate({
                query: query
              });
             
              for await (const event of subscription) {
                console.log(event);
             
                //Complete a running subscription by breaking the iterator loop
                break;
              }
        } catch (err) {
            console.log("ERROR OCCURRED!!");
            console.log(err);
        }
        
      })();

The above code results in 403. How to add the authorization headers to the outgoing HTTP upgrade messages before socket connection is established. Any help?

1

There are 1 answers

1
BryanJ On BEST ANSWER

You were close! You need to extend WebSocket when passing a custom implementation to webSocketImpl.

The documentation for graphql-ws has an example of implementing a custom client with headers and using it with createClient.

Applying this to your use case would be something like:

const WebSocket = require('ws');
import { createClient } from 'graphql-ws';

class WebSocketWithHeaders extends WebSocket {
  constructor(address, protocols) {
    super(address, protocols, {
      headers: {
        'User-Agent': 'graphql-ws client',
        'Authorization': '<token>',
        // or use a cookie like this
        //'Cookie': 'JWT=<token>'
      },
    });
  }
}

const sClient = createClient({
        url: graphqlWsServer,
        webSocketImpl: WebSocketWithHeaders
      });

      (async () => {
        try {
            const subscription = sClient.iterate({
                query: query
              });
             
              for await (const event of subscription) {
                console.log(event);
             
                //Complete a running subscription by breaking the iterator loop
                break;
              }
        } catch (err) {
            console.log("ERROR OCCURRED!!");
            console.log(err);
        }
        
      })();

edit: forgot to mention that this only works outside of the browser. see this post for details.