2

I'm having trouble with GraphQL subscriptions in Apollo. I want to subscribe to added "perspectives" on topics (basically added comments on posts), and I'm pretty sure I have the server set up correctly. The client is what's giving me trouble. (If this question looks familiar, I asked it before and thought I got an answer, but no go). Here is my subscription schema:

type Subscription {
  perspectiveAdded: Perspective
}

schema {
  query: RootQuery
  mutation: Mutation
  subscription: Subscription
}

My subscription resolver:

Subscription: {
    perspectiveAdded(perspective) {
      return perspective;
    }
  }

My subscriptionManager:

const pubsub = new PubSub();
const subscriptionManager = new SubscriptionManager({
  schema,
  pubsub,
  setupFunctions: {
    perspectiveAdded: (options, args) => {
      perspectiveAdded: {
        filter: (topic) => {
          return topic
        }
      }
    },
  }
});

export { subscriptionManager, pubsub };

The last part of my addPerspective mutation that is (the event trigger for the subscription):

//...    
return perspective.save((error, perspective) => {
   if(error){
     console.log(error);
   }

   //Publish it to Subscription channel
   pubsub.publish('perspectiveAdded', perspective);
});

And then I've wired up the actual server to support subscriptions:

const PORT = process.env.PORT || 4000;
const server = createServer(app);

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

I've wired up the client correctly as well, because in my terminal I see the "Websocket connection established" message. The part I'm stumped about is how to actually call the subscription. According to the Apollo blog, I should be able to test the subscription in GraphiQL (since I'm using an apollo server, now graphql-server-express), but it says "Resolve function for \"Subscription.perspectiveAdded\" returned undefined".

For my component, I've tried to wire up 'subscribeToMore' but in the browser console, I'm getting an error object that says "Invalid params returned from onSubscribe! return values must be an object!" I'm not sure which object it is referring to.

Here's my subscription query called perspectiveSubscription:

export default gql`
subscription {
  perspectiveAdded {
    id
    content
  }
}
`;

And the wired up component:

constructor(props){
      super(props);
      this.state = {};
      this.subscription = null;
    }



    componentWillReceiveProps(nextProps) {
      if (!this.subscription && !nextProps.data.loading) {
        let { subscribeToMore } = this.props.data
        this.subscription = subscribeToMore(
          {
            document: perspectiveSubscription,
            updateQuery: (previousResult, { subscriptionData }) => {
              if(!subscriptionData.data){
                console.log('no new subscription data');
                return previousResult;
              }

              const newPerspective = subscriptionData.data.perspectiveAdded;
              console.log(newPerspective);
              return Object.assign({}, previousResult, newPerspective);
            }
          }
        )
      }

From here, I get a message in my terminal saying the client has been subscribed, but still I get the error object mentioned above. I've been pulling my hair out about this for days - do you guys see what I am missing here? Specifically, any ideas on the client side? Thanks everyone!

1 Answers1

0

It seems like the server side is not correct, because the subscription is added and graphiql also does not deliver a correct result.

One thing that i suggest is that you check the channel definition:

const pubsub = new PubSub();
const subscriptionManager = new SubscriptionManager({
  schema,
  pubsub,
  setupFunctions: {
    perspectiveAdded: (options, args) => {
      perspectiveAdded: {
        filter: (perspective) => {
        console.log(perspective); // check if object is correct
          return true; // return true and not the object as long as you do not want to filter
        }
      }
    },
  }
});

export { subscriptionManager, pubsub };

And also check if the perspective object is saved and defined before the pubsub call. And i think you also want to add a comment id for which the subscription should be working. On my side it looks more or less like in this post

Community
  • 1
  • 1
Locco0_0
  • 3,420
  • 5
  • 30
  • 42
  • I've played around with the channel definition to no avail. But you're right that it's probably a problem with my server. I know that the mutation is called correctly and that the pubsub.publish() method is executed as well (I've console logged it). It seems the problem lies with my subscription resolver: `Subscription: { perspectiveAdded(perspective) { console.log('did this work?'); return perspective; } }` That console.log is not executed, and the error I get says the subscription resolve function returns undefined –  Apr 22 '17 at 15:32
  • Ok strange, you might want to check your executable schema. See if the subscription is inside. – Locco0_0 Apr 22 '17 at 18:57
  • `const executableSchema = makeExecutableSchema({ typeDefs: Types, resolvers: Resolvers, logger, allowUndefinedInResolve: false });` I define the subscription type and the schema type in Types up top, but are you saying I need something separate in here for subscriptions as well? –  Apr 22 '17 at 19:00
  • Okay, weird, when i use this as my resolve function and I pass in a toipcId as an argument (basically the ID of the post the comment belongs to), it resolves just fine: `Subscription: { perspectiveAdded: (root, {topicId}) => { return Perspective.findOne({topicId}) } }` But looking at Apollo's example, they seem to pass the entire comment object through to the Subscription resolve function...but I don't see how to do that –  Apr 22 '17 at 19:23
  • perfect if this works. If you look at the post that i added you can just use the root. It should be your obejct – Locco0_0 Apr 22 '17 at 20:06
  • ah okay we're getting there! I understand this now, but unfortunately my root is returning as undefined. It's supposed to be passed with this: `return pubsub.publish('perspectiveAdded', perspective);` right? I can log that perspective object, but for some reason it's not being passed to my subscription resolver. I know the resolver is being executed though, because I can now run a subscription query in GraphiQL and pass in the topicID argument. (This is how I'm seeing that the root is undefined). Unless I pass in the root object somewhere else I'm missing? –  Apr 22 '17 at 20:27
  • You are right. It should work as you described it. You might want to add the topic id in the subscription, because you know it on the client, correct? Are you sure that you are using this one pubsub instance to publish the value? – Locco0_0 Apr 23 '17 at 06:18
  • Okay found out that the pubsub instance in the file I call pubsub.publish() is actually importing as undefined. I'm wondering if I have a circular dependency somewhere, or if I'm importing something in the wrong order. The hunt begins... –  Apr 23 '17 at 14:53
  • Argh I fixed the pubsub import, but it's still not sending the payload to the subscription resolver. –  Apr 23 '17 at 20:58
  • All looks ok, so i don't think that i can help you... Maybe you can try to remove the subscriptions path from ` { server: server, path: '/', }`. Did not see this in other examples – Locco0_0 Apr 24 '17 at 05:48
  • I found out what was wrong - I was protecting my /graphql route with an authorization middleware, so that was preventing my pubsub instance from passing along the value. Only thing is now when i run subscriptions in graphiql, I just see [object Object]. At least we're getting somewhere though haha. I can't thank you enough for your patience and help in this! –  Apr 24 '17 at 22:54
  • Perfect! That is a though error to find. I guess now it will work with the client – Locco0_0 Apr 25 '17 at 06:41