react-relay-network-modern icon indicating copy to clipboard operation
react-relay-network-modern copied to clipboard

[Question]: Subscriptions don't close and don't receive data

Open stramel opened this issue 6 years ago • 4 comments

I'm running into issues using subscriptions-transport-ws with this. The subscriptions never close and don't seem to be getting data.

import { SubscriptionClient } from 'subscriptions-transport-ws'

// ...

const client = new SubscriptionClient('/api/graphql', {
  reconnect: true
})

function getSubscribeFn(client) {
  return (operation, variables) => {
    return client.request({ query: operation.text, variables, operationName: operation.name })
  }
}


// ...

{
  subscribeFn: getSubscribeFn(client)
  }
}

It would be nice to export a subscribeFn to properly handle subscriptions. It could easily just accept a SubscriptionClient instance.

stramel avatar Mar 26 '20 06:03 stramel

This doesn't seem like the correct solution but it is now properly closing subscriptions. Can someone confirm or provide a correct solution?

{
  subscribeFn: (operation, variables) => {
    return Observable.create(sink =>
      // @ts-ignore
      client.request({ query: operation.text, variables, operationName: operation.name }).subscribe(sink)
    )
  }
}

stramel avatar Mar 26 '20 08:03 stramel

This is the solution we're using, I clipped out our authentication and error handling. I haven't used Observables enough to say if it is equivalent to the solution you are using.

const client = new SubscriptionClient(wsUrl, { lazy: true, reconnect: true });

const subscribeFn = (operation, variables) => {
  const query = operation.text;
  if (!query) return;
  const request = client.request({ query, variables });
  return {
    subscribe: observer => {
      const subscription = request.subscribe(observer);
      observer.start(subscription);
    },
  };
};

AnotherHermit avatar Mar 26 '20 10:03 AnotherHermit

@AnotherHermit Thanks for the example of how you're handling it. It worked for me as well.

I did have some issues with the typings that said that dispose was required. Is there any interest in offering a minimal function that accepts a SubscriptionClient and returning a subscribeFn so users don't have to worry about this stuff?

This is basically what I'm thinking about.

function getSubscribeFn(client: SubscriptionClient) {
  return (operation: ConcreteBatch, variables: Variables) => {
    const query = operation.text
    if (!query) return

    const request = client.request({
      query,
      variables,
      operationName: operation.name,
    })

    return {
      subscribe: observer => {
        const subscription = request.subscribe(observer)
        observer.start(subscription)
      },
      dispose: () => {
        client.unsubscribeAll()
        client.close()
      },
    }
  }
}

stramel avatar Mar 26 '20 18:03 stramel

I've followed an article and ended up with this:

import { execute } from 'apollo-link';
import { SubscriptionClient } from 'subscriptions-transport-ws';
import { WebSocketLink } from 'apollo-link-ws';

const subscriptionClient = new SubscriptionClient('ws://localhost:4000/graphql', {
  lazy: true,
  reconnect: true,
});

const webSocketLink = new WebSocketLink(subscriptionClient);

const subscribeFn = (operation, variables) =>
  execute(webSocketLink, {
    query: operation.text,
    variables,
  });

Haven't deployed to production yet, but it seems to work well.

AlicanC avatar May 27 '20 13:05 AlicanC