apollographql / graphql-subscriptions

:newspaper: A small module that implements GraphQL subscriptions for Node.js
MIT License
1.59k stars 133 forks source link

Subscription not working. #188

Closed spyshower closed 5 years ago

spyshower commented 5 years ago

Hello,

I am trying to use subscriptions in my RN app. The following doesn't work:

import { PubSub, withFilter } from 'graphql-subscriptions';
const pubsub = new PubSub();

type Subscription {
  newDate(otherUserId: Int!): date
}
...

pubsub.publish("newDate", {
      newDate: {
            // other stuff
            otherUserId: otherUser.id,
            otherUserName: otherUser.name,
       },
       otherUserId: args.otherUserId
});

...
// resolver:
Subscription: {
      newDate: {
        subscribe: () => {
          console.log("help they can't see me")
           pubsub.asyncIterator('newDate')
        }
      }
},

I have been trying to fix this for way too many hours and still cannot get working; I see nothing logged.

Side question: Since the resolver is not fired, is there any chance the problem will be at the creation of the server or the client?

grantwwu commented 5 years ago

How are you subscribing?

spyshower commented 5 years ago

How are you subscribing?

You mean on the client?

grantwwu commented 5 years ago

Correct.

spyshower commented 5 years ago

App.js


const httpLink = new HttpLink({
  uri: 'http://192.168.43.135:8081/graphql'
});

const authLink = setContext(async (req, { headers }) => {

  let token = null

  try {
    token = await AsyncStorage.getItem('@jwt:key');
  }
  catch (error) {
    console.log("appcontainer no token")
  }

  return {
    ...headers,
    headers: {
      authorization: token ? `Bearer ${token}` : null,
    },
  };
});

let linkConcated = authLink.concat(httpLink);

const cache = new InMemoryCache({
  dataIdFromObject: object => object.key || null
});

const wsLink = new WebSocketLink({
  uri: 'ws://192.168.43.135:8082/',
  options: {
    reconnect: true
  }
});

const link = split(
  // split based on operation type
  ({ query }) => {
    const { kind, operation } = getMainDefinition(query);
    return kind === 'OperationDefinition' && operation === 'subscription';
  },
  wsLink,
  linkConcated,
);

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

MyComponent.js


componentDidMount = () => {

    this.props.data.subscribeToMore({
      document: newDateSubscription,
      variables: {
        otherUserId: this.props.otherUserId,
      },
      updateQuery: (prev, {subscriptionData}) => {

        console.log('am I invisible or what')
        if (!subscriptionData.data) {
          return prev;
        }

        const newDate = subscriptionData.data.newDate;
        console.log(newDate)
        return newDate
      }
  });
const newDateSubscription = gql`
  subscription newDate($otherUserId: Int!) {
    newDate(otherUserId: $otherUserId) {
      ....
      dateId
      otherUserId
      otherUserName
    }
  }
`;

Just to clarify, the date object contains more fields. This is correct though on my OP: newDate(otherUserId: Int!): date

grantwwu commented 5 years ago

What does the client see?

spyshower commented 5 years ago

What does the client see?

Neither server nor client see anything. The only "good" thing I can say is that the pubsub.publish is called...

grantwwu commented 5 years ago

Wait, shouldn't you be returning the AsyncIterator?

It's not very easy to debug like this. Could you upload a reproducing example?

spyshower commented 5 years ago

Ok I got it working. the problem was that I didn't have the same port number for subscriptions as normal queries. I also forgot to write '/subscriptions' after the port number. The example in apollo docs show the exact opposite of what I just wrote by the way. Nice.

However it's still not working properly. I get the following warning when the subscription fires on the client:

screenshot 2018-12-09 at 22 03 41

'getDates' is the name of the query that returns the dates of a user. So it doesn't have to be a field like dateId, status etc.

Below is the updated code:

type Subscription {
    newDate: date
  }
  Subscription: {

  newDate: {
    subscribe: withFilter(
      () => pubsub.asyncIterator('newDate'),
      (payload, variables) => {
        console.log('variables')
        console.log(variables)
        console.log('payload')
        console.log(payload)
       // returning true always for testing
        return true

      }
    )
  }
}, 
const newDate = {
            dateId: date.id,
            status: date.status,
            sender: date.user1, 
            place: date.place,
            hour: date.hour,
            otherUserId: otherUser.id,
            otherUserName: otherUser.name,
          }

pubsub.publish('newDate', { newDate: newDate, otherUserId: args.otherUserId });

The client's component code is the same. Although I am a bit confused what the argument on the subscription means, ie. it specifies 'who am I' or 'who is the notification for'? Also, when I use withFilter the payload and variables are undefined and empty respectively (this is why I can't experiment with the argument).

grantwwu commented 5 years ago

https://github.com/apollographql/graphql-subscriptions/issues/123 may be related to the missing field issue

As for why you're getting undefined and empty, that may be related to https://github.com/apollographql/graphql-subscriptions/issues/132

I have a fix merged but I'm not going to push for it to be released until I can find time to write tests for it. Help is definitely welcome here...

What do you mean by "argument on the subscription"? You mean what you're using 'newDate' here for? Well it's just a name - you can have multiple of them, and they won't interfere with each other. When you use something like Kafka or Pulsar or Google PubSub or Redis etc. they might map to topic names or some other concept in the pubsub system.

spyshower commented 5 years ago

I don't remember what I changed but it seems to be working good now. By "argument on the subscription" I meant the 'otherUserId' so only the right user gets notified about the date. I got that working too.

Appreciate your help @grantwwu

rmlevangelio commented 2 years ago

I'm also curious about this. If I don't use withFilter, it actually works properly. But of course, we wanted to filter the data since we don't want to send any response to other sessions. But as @spyshower said, when he used withFilter, it doesn't log the variables and payload that's why it's hard to debug.

nodejavascript commented 2 years ago

hi,

did you get to finally work this out?

skizzo commented 2 months ago

+1, not getting any client data (vars) in my subscription filters.