graphql-bookshelf
graphql-bookshelf copied to clipboard
expected iterable, but did not find one for field Query.XXXX
Hello! I'm trying to wire up a User model with two GraphQL query fields.
The type:
const userType = new GraphQLObjectType(BookshelfType({
name: 'User',
description: 'A user',
fields: model => ({
id: globalIdField('User'), // Relay stuff
email: model.attr({
type: GraphQLString,
}),
role: model.attr({
type: GraphQLString,
}),
}),
interfaces: [nodeInterface], // Relay stuff
}));
The schema:
const queryType = new GraphQLObjectType({
name: 'Query',
fields: () => ({
node: nodeField,
users: {
type: new GraphQLList(userType),
resolve: () => User.fetchAll(),
},
user: {
args: {
id: { type: GraphQLInt },
},
type: userType,
resolve: (_, args) => User.forge({ id: args.id }).fetch(),
},
}),
});
export default new GraphQLSchema({
query: queryType,
});
I can query one user just fine with { user(id: 1) { id, email, role } }
.
However, if I query the all users field { users { id, email, role } }
I get this error: User Error: expected iterable, but did not find one for field Query.users.
I guess it's complaining about type: new GraphlQLList(userType)
as return type? It's expecting an iterable but gets a Bookshelf Collection, right?
If I force it to return an array instead of a collection it works:
// I change
resolve: () => User.fetchAll()
// to:
resolve: () => User.fetchAll().then(users => users.reduce((arr, user) => { arr.push(user); return arr; }, [])),
What am I doing wrong here?
I ran into a very similar issue. I would expect User.fetchAll() to work, though yes instead of an iterable GraphQL is getting a Promise<Collection> (Promises are fine, as GraphQL will execute the .then on the promise). The way I got around this would be by doing the following:
activities: {
type: new GraphQLList(ActivityType),
resolve: async () => {
return await Activity.fetchAll(...options...).then(function (collection) {
return collection.models;
});
}
}
Personally I think that this library should be resolving the collection into a list of things, but that might just be me @brysgo what do you think?
@tmack8001 Thanks for replying! I don't think the promise itself is a problem, but your better insight into Bookshelf collections sizes down the problem to:
resolve: () => User.fetchAll().then(collection => collection.models),
So you actually don't have to do async
and await
, just converting collection
to collection.models
is all that it takes to be valid.
@tmack8001 - There are plenty of ways to make this library more convenient and I'm open to adding as many as possible.
Of course, I am just starting to look at graphQL for a personal project I have been meaning to work on so as I get dirtier with it I will ping out with questions or requests.
Do you think we should encapsulate this Collection -> Iterable within this library? If so, I can work on getting a PR together after diving into things.
I've also been thinking about https://github.com/brysgo/graphql-bookshelf/issues/5 a little bit, as I'm building pagination into my implementation returning a list. That we would also probably bake in for ease of use for adopters.
I think encapsulating the collection is a great idea. As soon as you have something, put it up and I'll check it out.
As for #5, I've been working with relay more so if you have any relay helpers you end up making I'd be happy to take a look also.
Thinking about this further, how would one go about doing this. Since in the schema definition we aren't using any encapsulation with this library. We could add a helper around the relationship, but I don't see another way since the query type isn't related to graphql-bookshelf.
Right, we would need to provide proxies to bookshelf classes that add functionality.
Alternatively, we could wrap every resolve on a bookshelf type and check for collections that get returned.
in feathersjs case, @prewk answer works well.
see:
users(root, args, context) {
return Users.find({}).then(results => results.data)
}