graphql-subscriptions icon indicating copy to clipboard operation
graphql-subscriptions copied to clipboard

Have to publish with subscription name in the object

Open AnthonyCC opened this issue 7 years ago • 12 comments

I was following the tutorial in the README file. I noticed if I use

// publish to the channel
pubsub.publish('newCommentsChannel', {
  id: 123,
  content: 'Test',
  repoFullName: 'apollostack/GitHunt-API',
  posted_by: 'helfer',
});

I will get

{
  data: {
     commentAdded: null
   }
}

But if I publish with the subscription name in the object

// publish to the channel
pubsub.publish('newCommentsChannel', {
  commentAdded: {
    id: 123,
    content: 'Test',
    repoFullName: 'apollostack/GitHunt-API',
    posted_by: 'helfer',
  }
});

Then I will get the result as in the README. Am I missing something from the document?

AnthonyCC avatar Mar 17 '17 22:03 AnthonyCC

Can you post a link to a repo that duplicates this issue? Depending on how you are constructing your schema you may have to attach a custom resolver to the subscription.commentAdded field.

NeoPhi avatar Mar 27 '17 14:03 NeoPhi

the implementation is abit bugy, each time a new value emits, it will run graphql query and return the result. when running the graphql query, it will pass the emitted value from pubsub as root. the right thing to do if you don't want to write a resolver is make sure the root will be passed as: { [channelName]: value } and not just value.

DxCx avatar Mar 27 '17 14:03 DxCx

Sure, this is the link to my repo. https://github.com/AnthonyCC/graphQL-Demo

The schema is defined in /server/graphQL/schema.js.

The data is published to the channel in /server/route/graphQL.js, updateUserPassword.

Thanks for looking into this issue.

AnthonyCC avatar Mar 28 '17 19:03 AnthonyCC

In your example the object you are publishing is nested incorrectly. sub.pubsub.publish('passwordUpdatedChannel', {passwordUpdated:user}); should be: sub.pubsub.publish('passwordUpdatedChannel', user);

This example shows the behavior:

const graphql = require('graphql');
const sub = require('graphql-subscriptions');

const schema = graphql.buildSchema(`
  type Subscription {
    passwordUpdated: User
  }
  type Query {
    getMessage: String,
  }
  type User {
    name: String,
    password: String
  }
`);

const pubsub = new sub.PubSub();
const manager = new sub.SubscriptionManager({
  schema,
  pubsub,
  setupFunctions: {
    passwordUpdated: (options, args) => ({
      passwordUpdatedChannel: { filter: user => user != null },
    }),
  },
});

manager.subscribe({
  query: `subscription passwordUpdated { passwordUpdated { name password } }`,
  context: {},
  callback: (err, data) => console.log('callback', err, data),
});

pubsub.publish('passwordUpdatedChannel', {
  passwordUpdated: { name: 'name', password: 'password' },
});

NeoPhi avatar Apr 03 '17 14:04 NeoPhi

I had the same issue and fixed it by adding a Subscription resolver to my resolver map

In your case this woule be

const resolvers = {
  Subscription: {
    commentAdded: comment => comment,
  },
}

In the documentation it says:

Create a resolver just like queries and mutations if you need to perform a logic over the published data

For me it did not work at all without the resolver. If someone can confirm this I am going submit a PR.

n1ru4l avatar Apr 03 '17 17:04 n1ru4l

Yes. @NeoPhi , that is the issue I am having. If I publish the data with sub.pubsub.publish('passwordUpdatedChannel', user);

Then I won't get the data in the subscriber, I will get

{
  data: {
     passwordUpdatedChannel: null
   }
}

@n1ru4l , adding a subscription resolver does resolve the issue for me. Thanks for sharing your finding.

I have two questions

  1. Do I need to have the subscription resolver even though I don't need perform any logic over the published data, other than just return the data?
  2. Is there a way to add subscription resolver in graphql-js (which is the library I am using on the server side)? Or I have to use the Apollo's one?

AnthonyCC avatar Apr 03 '17 22:04 AnthonyCC

@AnthonyCC When I run the code listed above the output is: callback null { data: { passwordUpdated: { name: 'name', password: 'password' } } } So I'm unclear what you mean by the subscriber not getting the data.

NeoPhi avatar Apr 04 '17 01:04 NeoPhi

@NeoPhi ,

If you publish the object with the subscription name nested in the object, then you will get the expected output, which iscallback null { data: { passwordUpdated: { name: 'name', password: 'password' } } } . As in you example listed,

pubsub.publish('passwordUpdatedChannel', {
  passwordUpdated: { name: 'name', password: 'password' },
});

You have to nest the object with passwordUpdated

However, if you publish the data by sub.pubsub.publish('passwordUpdatedChannel', { name: 'name', password: 'password' });, then you will get callback null { data: { passwordUpdated: null } }

AnthonyCC avatar Apr 04 '17 17:04 AnthonyCC

I found this while struggling with something similar - I wanted to use a certain property in the filter option that I didn't want to publish over the subscription. In my case the behavior described here was actually useful: I was able to publish an object with the real subscription data under the subscription name, and some extra properties that didn't get sent through the subscription.

That being said, it's too implicit imo. Maybe it'd be nice to have a map option (just like we have filter) where we can convert whatever we publish to pubsub into something else to send as the subscription result.

julioolvr avatar Apr 16 '17 14:04 julioolvr

Finally. Found this issue and the problem that I was trying to solve for more than an hour is gone. None of the tutorials mentioned that you have to nest it inside the publish method and match the key with the subscription name.

mb8z avatar Aug 16 '17 23:08 mb8z

Please add this to readme! I spent hours...

mjasnikovs avatar Aug 21 '17 16:08 mjasnikovs

Keeping this open because it seems like we could use more documentation on this.

grantwwu avatar Oct 02 '18 15:10 grantwwu