ApolloLink to make a new websocket connection for each subscription operation name?

I am trying to figure out how to make a new websocket connection for each subscription and I thought maybe ApolloLink would be the answer. I’m having trouble making it work. I know my code is not correct but I’m a bit lost on the direction I should take. Any advice is greatly appreciated.

I’m trying to do something like this.

const httpLink = new HttpLink({
  uri: process.env.REACT_APP_LIVE_SERVICE_API,
  credentials: 'include'
});

const subscriptionLinks = new ApolloLink((operation, forward) => {
  let cars;
  let people;
  let books;

  if (operation.operationName === 'CarsSubscription') {
    cars = new WebSocketLink({
      uri: `wss://${process.env.REACT_APP_LIVE_API.replace('https://', '')}/subscriptions`,
      credentials: 'include',
      options: {
        reconnect: true,
        lazy: true,
        wsOptionArguments: []
      }
    });
    forward(cars);
  }

  if (operation.operationName === 'PeopleSubscription') {
    people = new WebSocketLink({
      uri: `wss://${process.env.REACT_APP_LIVE_API.replace('https://', '')}/subscriptions`,
      credentials: 'include',
      options: {
        reconnect: true,
        lazy: true,
        wsOptionArguments: []
      }
    });
    forward(people);
  }

  if (operation.operationName === 'BooksSubscription') {
    books = new WebSocketLink({
      uri: `wss://${process.env.REACT_APP_LIVE_API.replace('https://', '')}/subscriptions`,
      credentials: 'include',
      options: {
        reconnect: true,
        lazy: true,
        wsOptionArguments: []
      }
    });
    forward(books);
  }

  return ApolloLink.from([cars, people, books]);
});

const splitLink = split(
  ({ query }) => {
    const definition = getMainDefinition(query);

    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    );
  },
  subscriptionLinks,
  httpLink
);

const client = new ApolloClient({
  link: splitLink,
  cache
});

I might be missing something but what exactly is the reason for having more than 1 WebSocket connection, 1 per Subscription?

You can publish your data to different channels based on the subscription, so wouldn’t 1 WebSocket connection per client suffice?

That is a fair question. The reason is because of limitations on the back end which is owned by another team. If using one websocket connection, the last subscription will override anything prior, so only the last subscription gets any updates. To your point, this is not ideal and more expensive than need be. This is just a temporary solution until the back end team fixes the implementation on the server to allow for multiple channels.

As an update I did figure out how to make this work using nested splits, again - not ideal but it does “work”. There is probably a better way to do it, but since this is a temporary solution I can live with it for now.

const cache = new InMemoryCache();

const httpLink = new HttpLink({
  uri: process.env.REACT_APP_LIVE_API,
  credentials: 'include'
});

const webSocketOptions = {
  uri: `wss://${process.env.REACT_APP_LIVE_API.replace('https://', '')}/subscriptions`,
  credentials: 'include',
  options: {
    reconnect: true,
    lazy: true,
    timeout: 300000
  }
};

const splitLink = split(
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    );
  },
  split(
    ({ operationName }) => operationName === 'CarsSubscription',
    new WebSocketLink(webSocketOptions),
    split(
      ({ operationName }) => operationName === 'PeopleSubscription',
      new WebSocketLink(webSocketOptions),
      split(
        ({ operationName }) => operationName === 'BooksSubscription',
        new WebSocketLink(webSocketOptions)
      )
    )
  ),
  httpLink
);

const client = new ApolloClient({
  link: splitLink,
  cache
});
1 Like