I'm trying to get the resolver for a subscription working in Apollo Server 2. The subscription works when it is a top-level field (i.e. directly under Subscription at the schema root).
However, if the subscription is contained within another type, I always get the error Subscription field must return Async Iterable. Received: undefined on the client's websocket connection -- the server's resolver is never executed.
i.e. this schema works:
type Subscription {
postAdded: Post
}
but this one does not:
type Subscription {
post: PostSubscription
}
type PostSubscription {
postAdded: Post
}
My resolver for the second case looks like this, but I've tried a bunch of different variations with no success:
Subscription: {
post: () => ({
PostSubscription: {}
})
},
PostSubscription: {
postAdded: {
subscribe: () => pubSub.asyncIterator(['postAdded'])
}
}
The error message means your post resolver
Subscription: {
post: () => ({
PostSubscription: {} // This needs to return AsyncIterator
})
},
If I understand you correctly, you want to subscribe a postAdded, postDeleted, postUpdated, all three under the post. I understand you want to try to namespace them under the same model, which help organization better. But it has some issues, I will explain them later.
One sentence advice: it's better to have these 3 fields directly under the root subscription field.
Not to say you cannot do it, but if you really want, Suppose you are doing a subscription
Subscription{
post{
postAdded: Post
postDeleted: Post
postUpdated(id:Int!): Post
}
}
Then the three nested fields are all "sharing" the same channel.
Then there's a couple of things you need to do
Subscription: {
post: {
subscription: () => pubSub.asyncIterator(['postChannel'])
}
}
something like this
Mutation{
createPost: (_,args,context,info)=>{
const createdObject = // do create
pubsub.publish("postChannel", {
post:{
// do not do postUpdate, postDelete, because there's nothing updated, deleted
postAdded:createdObject
}
})
}
}
This will make what you want sort of works, but this has a couple of issues. 1. Any time any update/create/delete happened, the client is notified. Potentially this will give client incorrect information. Like so If a client subscribe with
subscription{
post{
postAdded
}
}
Then when some body else updated a post, the client will get a response like this
response = {
subscription:{
postAdded:null
}
}
This is probably ok to ignore null for postAdd. But it will definitely be a problem for postUpdate. Imagine a user subscribe to
subscribe{
post{
postUpdate(id:1)
}
}
Then somebody added post, the client will always get notified, because the three events shared the same channel. Then he will receive
response = {
subscription:{
postUpdated:null
}
}
Then, if you are using apollo client, it will remove the post:1 from the cache because it will think that post is null.
Because of these issues, It is highly recommended create many channels, preferably three channels per model. And create three root level subscriptions instead of nesting them.
For a minimum working subscription, I would redirect you to a git-repo that I made to demonstrate subscription https://github.com/hansololai/apollo_subscription_boilerplate
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With