graphql-bookshelf icon indicating copy to clipboard operation
graphql-bookshelf copied to clipboard

expected iterable, but did not find one for field Query.XXXX

Open prewk opened this issue 8 years ago • 8 comments

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?

prewk avatar Mar 27 '16 21:03 prewk

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 avatar Mar 27 '16 22:03 tmack8001

@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.

prewk avatar Mar 28 '16 07:03 prewk

@tmack8001 - There are plenty of ways to make this library more convenient and I'm open to adding as many as possible.

brysgo avatar Mar 28 '16 19:03 brysgo

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.

tmack8001 avatar Mar 28 '16 20:03 tmack8001

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.

brysgo avatar Mar 29 '16 01:03 brysgo

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.

tmack8001 avatar Apr 01 '16 16:04 tmack8001

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.

brysgo avatar Apr 02 '16 00:04 brysgo

in feathersjs case, @prewk answer works well.
see:

users(root, args, context) {
  return Users.find({}).then(results => results.data)
}

zeusbaba avatar Oct 31 '17 13:10 zeusbaba