1
votes

I've followed Apollo's docs for setting up GraphQL subscriptions on both the client and server, and though I'm 90% there, I can't figure out how to set up subscription channels and how to connect mutations to those channels so that whenever a mutation occurs, the server pushes the new data to the client. (For content, I'm making a Reddit clone where people post topics and others comment on it. So when you see "Topics" or "TopicList," think of those as posts.)

So far, I have set up Apollo Client for subscriptions successfully:

const wsClient = new SubscriptionClient('ws://localhost:3001/subscriptions', {
  reconnect: true
});

const networkInterface = createNetworkInterface({
    uri: '/graphql',
    opts: {
        credentials: 'same-origin'
    }
});

const networkInterfaceWithSubscriptions = addGraphQLSubscriptions(
  networkInterface,
  wsClient,
);

const client = new ApolloClient({
    networkInterface: networkInterfaceWithSubscriptions,
    dataIdFromObject: o => o.id
});

And I've set up my back-end for subscriptions as well. Here's my server.js file:

//===========================================================
//Subscription Managaer
//===========================================================
const pubsub = new PubSub();
const subscriptionManager = new SubscriptionManager({
  schema: schema,
  pubsub: pubsub
});

//=====================================
//WebSocket + Express Server
//=====================================

const server = createServer(app);

//setup listening port
server.listen(3001, ()=>{
    new SubscriptionServer(
    {
        subscriptionManager: subscriptionManager,
        onConnect: (connectionParams, webSocket) => {
            console.log('Websocket connection established');
        },
        onSubscribe: (message, params, webSocket) => {
            console.log("The client has been subscribed", message, params);
        },
        onUnsubsribe: (webSocket) => {
            console.log("Now unsubscribed");
        },
        onDisconnect: (webSocket) => {
            console.log('Now disconnected');
        }
    }, 
    {
        server: server,
        path: '/subscriptions',
    });
    console.log('Server is hot my man!');
})

I know these are successful, because I get the "Websocket connection established" message logged in my terminal.

Next is the actual subscription - I've created a subscription schema type (just like queries and mutations):

const SubscriptionType = new GraphQLObjectType({
    name: 'Subscription',
    fields: () => ({
        topicAdded: {
            type: TopicType,
            args: {repoFullName: {type: GraphQLString}}, //I don't understand what repoFullName is - I was trying to follow the Apollo docs, but they never specified that
            resolve(parentValue, args){
                return parentValue;
            }

        }
    })
});

module.exports = SubscriptionType;

and incorporated it into my root schema. So when I check out GraphiQL, I see: this subscription available in the Docs side menu My GraphiQIL UI showing the subscriptionSchema successfully

In my React component, I successfully 'subscribe' to it using Apollo's subscribeToMore method:

const TOPICS_SUBSCRIPTION = gql`
    subscription OnTopicAdded($repoFullName: String){
        topicAdded(repoFullName: $repoFullName){
            id
        }
    }
`;

class TopicList extends Component {
    componentDidMount() {
        this.createMessageSubscription = this.props.data.subscribeToMore({
          document: TOPICS_SUBSCRIPTION,
          // updateQuery: (previousState, {subscriptionData}) => {
          //   const newTopic = subscriptionData.data.Topic.node
          //   const topics = previousState.findTopics.concat([newTopic])
          //   return {
          //     findTopics: topics
          //   }
          // },
          onError: (err) => console.error(err)
        })

      } //...

And I get my "The client has been subscribed" message logged into my terminal. But this is where I'm stuck. I've read about the SetupFunction for the SubscriptionManager, but that isn't included in Apollo's docs. And I can't find how to map a 'createTopic' mutation to this subscription so that whenever someone adds a new topic, it pops up in TopicList.

I realize this is really long, but I've been pulling my hair out tyring to figure out what's the next step. Any help would be much appreciated!! Thank you for reading!

1

1 Answers

1
votes

Yes you are missing the setup function. You could take a look at this links GraphQL subscription docu or this example.

How it should work: First you need the channel on which you publish the changed data. In your case it could look like this:

const manager = new sub.SubscriptionManager({
  schema,
  pubSub,

  setupFunctions: {
    topicAdded: (options, args) => ({ // name of your graphQL subscription
      topicAddedChannel: { // name of your pubsub publish-tag
        filter: (topic) => {
          console.log(topic); //should always show you the new topic if there is a subscribed client
          return true; // You might want to add a filter. Maybe for guest users or so
        },
      },
    }),
  },
});

And here you see the need of the args: {repoFullName: {type: GraphQLString}} argument in the subscription. If you want to filter the subscription dependent on the "repoName". Meaning that only the client with a subscription with the "repoName" as argument gets an update.

Next you need a place where you call the pubsub.publish function. In your case after the add topic mutation has passed. Could look like this:

...

const topic = new Topic(/* args */);
topic.save((error, topic) => {
  if (!error) {
    pubsub.publish("topicAddedChannel", topic);
  }
  ...
});


....