I'm trying to implement subscriptions with apollo-server-express. From graphql-playground I receive the following error...
{
"error": {
"message": "Subscription field must return Async Iterable. Received: undefined"
}
}
Until now I had not had problems with queries and mutations. I also had no errors with subscriptions using apollo-server. But I do have them with apollo-server-express.
This is the code:
const {gql, ApolloError, PubSub} = require('apollo-server-express');
// Mongoose models
const NoteModel = require('../../mongoose/NoteModel');
// Instance PubSub (publications and subscriptions)
const pubsub = new PubSub();
// Subscriptions tags
const NOTE_ADDED = 'NOTE_ADDED';
// Type definitions define the "shape" of your data and specify
// which ways the data can be fetched from the GraphQL server.
const typeDefs = gql`
type Note {
_id: Int
title: String
description: String
}
input NoteInput {
title: String
description: String
}
# The "Query" type is the root of all GraphQL queries.
# Using extend is particularly useful in avoiding a large
# list of fields on root Queries and Mutations
# More info: https://www.apollographql.com/docs/graphql-tools/generate-schema#extend-types
extend type Query {
listNotes: [Note]
}
extend type Mutation {
addNote(input: NoteInput) : Note!,
updateNote(
id: Int!,
input: NoteInput
): Note!,
deleteNote(id: Int!): Boolean!
}
extend type Subscription {
noteAdded: [Note]
}
`;
const resolvers = {
Query: {
listNotes: async (parent, args, context, info) => {
}
},
Mutation: {
addNote: (parent, args, context, info) => {
pubsub.publish(NOTE_ADDED, { noteAdded: args.input });
return NoteModel.create(args.input);
},
updateNote: (parent, args, context, info) => {
},
deleteNote: (parent, args, context, info) => {
}
},
Subscription: {
noteAdded: {
subscribe: () => pubsub.asyncIterator([NOTE_ADDED]),
},
},
};
module.exports = {
typeDefs,
resolvers
};
In the definition of the scheme I call the typedefs and the resolvers and additionally what is required for the subscriptions to work in apoll-server-express
server.applyMiddleware({app});
const httpServer = http.createServer(app);
server.installSubscriptionHandlers(httpServer);
httpServer.listen(PORT, () => {
console.log(`๐ Server ready at http://localhost:${PORT}${server.graphqlPath}`);
console.log(`๐ Subscriptions ready at ws://localhost:${PORT}${server.subscriptionsPath}`);
});
๐ I'll close this since this doesn't appear to be a bug with Apollo Server, but rather a question about how to use it or one of its components.
Rather than asking it here in GitHub Issues โ where efforts are focused on fixing bugs and adding new features โ I'd ask that you take this question to the _Apollo Server_ channel within the Apollo community on Spectrum.chat where there are community members who might be able to relate to a similar problem, or might be able to help you out more interactively. Thanks for your understanding!
I solved this by passing typedefs and resolvers to ApolloServer instead of passing them as modules.
const server = new ApolloServer({
typeDefs: [NoteType, BookType],
resolvers: [NoteResolver, BookResolver],
})
Before this, the syntax was as follows.
const server = new ApolloServer({
modules: [
require('./schema/notes/'),
require('./schema/books/'),
],
})
It should be mentioned that queries and mutations with the syntax
modules: [
require('./schema/notes/'),
require('./schema/books/'),
],
work well, however with subscriptions there is an error.
I think I am running into this same issue with subscriptions and Typescript.
@Falieson I already solved it, I think you could reproduce the error in a repository and that might help you.
@abernix It is definitely a bug! And I did some investigation.
@LaurenceM10 wrote that it works with new ApolloServer({ typeDefs, resolvers }), but it does not with new ApolloServer({ modules })
In my case it does not work with new ApolloServer({ schema })
So, I found where the problem appears:
http://center.koryagin.com/i/2019-08-04_00:29:39_6df691.png
And with this trivial monkey-patching everything is working, subscription messages arrive in browser.
const schema = buildFederatedSchema(federationSources);
schema._subscriptionType._fields.messageCreated.subscribe =
federationSources[11].resolvers.Subscription.messageCreated.subscribe;
Plus, it needs a dummy resolver in this case.
Eventual project-level fix looks like this (_ is lodash):
const schema = buildFederatedSchema(federationSources);
// Hooray! Monkey-patching!
federationSources.forEach(({ resolvers }) => {
if (resolvers.Subscription) {
_.forEach(resolvers.Subscription, (resolver, field) => {
if (_.isPlainObject(resolver) && resolver.subscribe) {
const target = schema._subscriptionType._fields[field];
// Ensure everything is still as bad, as we expect
if (target.subscribe) {
throw new Error('The library might be fixed. Delete this patch.');
}
// Do patch
target.subscribe = resolver.subscribe;
if (!target.resolve) {
target.resolve = x => x;
}
}
});
}
});
Can someone please open this issue? this still happens a year after it was reported and closed.
Most helpful comment
@abernix It is definitely a bug! And I did some investigation.
@LaurenceM10 wrote that it works with
new ApolloServer({ typeDefs, resolvers }), but it does not withnew ApolloServer({ modules })In my case it does not work with
new ApolloServer({ schema })So, I found where the problem appears:
http://center.koryagin.com/i/2019-08-04_00:29:39_6df691.png
And with this trivial monkey-patching everything is working, subscription messages arrive in browser.