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.
@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
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…
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 Task
s 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 _id
s, and Apollo relies on _id
s 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.
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.
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()
.