DDP-Apollo 1.0.0-beta released with Apollo 2.0 support!

That’s great thanks for sharing @jamiter! I’m surprised the PR didn’t go through. I think a tech fall behind if the team behind it did, as long as the team is active, up to date and believing in their vision, the code will follow.

1 Like

@tab00, although fetchQueries might update the app for the current user, I don’t think it will update the page for other users. That’s where subscriptions come into play. Right?

Yes, you are right, so it depends on the use case.

For data that needs to be pushed to many users, subscriptions would be required. The graphql-subscriptions documentation makes it seem that the default PubSub is even less scalable than Meteor’s subscriptions. Though it feels quite inconvenient to need to set up (and pay for) Redis / MQTT / Kafka etc.

So if we need subscriptions for a production app that uses Apollo, are there any other options?

Well, there is the option to use Mongo for it. You could create a collection where you publish changes to and listen to items getting added on all servers. This is possible, because Meteor is of course able to listen to collection changes. With the right indexes, old messages will automatically be cleaned up. Make this use the PubSubEngine interface, open source it and you’ve made the life of Meteor developers easy again.

We are so spoiled with Meteor, because everything is done for us :laughing:

2 Likes

Looks like some work on a Mongo GraphQL Subscription already started here:

Work on it stopped a year ago though, but it’s still interesting as a reference. I like the fact that it’s not Meteor specific, but directly uses the oplog. It doesn’t allow triggering a custom event though. Maybe I can mix it with I had in mind…

1 Like

That looks very interesting. Author is @seba in the forums here.

Cool, I had totally forgotten about that! But that was made in the early days of Apollo to get a feeling on how it compares against meteor. I haven’t followed Apollo / Graphql development since, so no idea if any of it is still relevant.

In my exercise to try to rewrite simple-todos-react to use Apollo via ddp-apollo I haven’t been able to get subscriptions working (using PubSub from graphql-subscriptions) to update the list on the client on insert or removal of a task.

A subscription does works fine for updating the client on changes within individual tasks (e.g. checked, private, text).

Here is my code:

In typeDefs:

  type Subscription {
      tasksChanged: [Task]
  }

In resolvers:

Subscription: {
        tasksChanged: {
            subscribe: () => pubsub.asyncIterator(subChannelTasksChange),
        },
    },

In a mutation:

        taskRemove: (root, args, context) => {
            Tasks.remove(args.taskId);

            pubsub.publish(subChannelTasksChange, {
                tasksChanged: Tasks.find({}, { sort: { createdAt: -1 } }).fetch(),
            });
        },

subscribe call in client component:

componentWillMount() {
    import gql from 'graphql-tag';

    this.props.client.subscribe({
      query: gql`
      subscription tasksChanged {
        tasksChanged {
          _id
          text
          createdAt
          owner
          username
          checked
          private
          }
      }
      `,
    }).subscribe({
      next() {
      },
    });
  }

In the Apollo Client store I can actually see the array of Tasks of the subscription and a new one does appear when I add a task via the UI, and the correct element does disappear when I delete a task from the UI:

ROOT_SUBSCRIPTION
tasksChanged: [Task]
0:▾Task:cLFEuGbTRkQ4BWfMR
1:▾Task:6ALZsoicJGunMBzEF
2:▾Task:cQuds8WPS5SHW45AZ
3:▾Task:DSb7Bt2qqSTN4GdAw
4:▾Task:KXLBapYE8yCLCAhYD
5:▾Task:kvs7BZbv3hS2QMC97
6:▾Task:wdNEnPgbXG4jZMutY
7:▾Task:ABSQo5NPxm8sZBv55
8:▾Task:Ldak6swMS5AuXLCdd
9:▾Task:y4SWjXpZdurKqxX7R

However the list on the UI does not reflect the state of this array, unless I reload the page.

Is the problem in my code or somewhere else?

In case it’s relevant, here is the getTasks query in the client component:

import { graphql, compose } from 'react-apollo';
import { withApollo } from 'react-apollo';

export default withApollo(compose(
  graphql(gql`
  query getTasks {
    tasks {
      _id
      text
      createdAt
      owner
      username
      checked
      private
      }
  }
`,
    {
      props: ({ data: { error, loading, tasks, refetch } }) => {
        if (loading) return { tasksLoading: true };
        if (error) return { hasErrors: true };

        return {
          tasks,
          refetch,
        };
      },
    },
    { name: 'getTasks' }
  ),
)(App));

The next() call in subscribe executes on every new change detected.
Adding { data } to the next() call allows access to the new correct data.
Would it be possible to update the UI here?
The problem is that this is a different context to the react component. How can we access the component context in order to modify it?

componentWillMount() {
    import gql from 'graphql-tag';

    this.props.client.subscribe({
      query: gql`
      subscription tasksChanged {
        tasksChanged {
          _id
          text
          createdAt
          owner
          username
          checked
          private
          }
      }
      `,
    }).subscribe({
      next({ data }) {
        //data.tasksChanged is the correct new array of Tasks. Can we update the UI here?
      },
    });
  }

It looks like the problem I had with UI not updating after items were added or removed was that arrays don’t have _ids, and Apollo relies on _ids for updating the UI. So instead of publishing an array, I changed the code to publish an object that contains an array and gave the object an _id field. That solved the problem.

1 Like

Thanks for diving into this! Maybe something to report at the Apollo Client repo. I’m not sure what the best practices are for such a use case and if the Apollo team is aware of the current implementation implications.

1 Like

Thanks to you too for the package. It has given me a chance to try Apollo without as much fuss as I would have needed to go through.

I’m still wondering how to access the React component context from within next({ data }) in subscribe(), in case I want to use the new data to update the component’s state via setState().