graphql-compose-mongoose icon indicating copy to clipboard operation
graphql-compose-mongoose copied to clipboard

Create a custom FindOrCreate resolver

Open mdegrees opened this issue 7 years ago • 5 comments

First off, thank you so much for the awesome work done here. So much cool stuff!

My issue is around creating a findOrCreate Graphql-compose custom resolver. The goal is basically checking before creation if the item exists. If so, the resolver should only return the existing item otherwise create it.

Is that possible, using uniquely pre-built resolvers?

Here is what i have tried:

_UserTC.addResolver({
  name: 'findOrCreate',
  type: _User.getResolver('createOne').getType(),
  args: _User.getResolver('createOne').getArgs(),
  resolve: ({ source, args, context, info }) => {
    User.findOne(args.record , (err, user) => {
       if (user)
       return user;
       
       else {
       user = new User (args.record)
       return user;
       }

    })
  }
}) 

Thank you in advance!

mdegrees avatar Jan 18 '18 17:01 mdegrees

Yep, it possible. You almost did the trick. The following solution should work:

_UserTC.addResolver({
  name: 'findOrCreate',
  kind: 'mutation',
  type: _User.getResolver('createOne').getType(),
  args: _User.getResolver('createOne').getArgs(),
  resolve: ({ source, args, context, info }) => {
    // must return result
    return User.findOne(args.record).then(user => {
      // check or create new user
      if (user) return user;
      return User.create(args.record);
    }).then(user => {
      // return to resolver proper shape of data (how in described in  _User.getResolver('createOne').getType() )
      return {
         record: user,
         recordId: _UserTC.getRecordIdFn()(user)
      };
    });
  }
}); 

The same code with async/await:

_UserTC.addResolver({
  name: 'findOrCreate',
  kind: 'mutation',
  type: _User.getResolver('createOne').getType(),
  args: _User.getResolver('createOne').getArgs(),
  resolve: async ({ source, args, context, info }) => {
    let user = await User.findOne(args.record).exec();
    if (!user) user = await User.create(args.record);

    return {
      record: user,
      recordId: _UserTC.getRecordIdFn()(user)
    };
  }
});

You miss just one thing - resolve function must return some data. And the shape of this data must have the same shape (fileds and types) which provided to type: _User.getResolver('createOne').getType(),.

nodkz avatar Jan 19 '18 05:01 nodkz

Forgot to say you about finding existed record

User.findOne(args.record)

will not work properly if you have nested fields eg. geo: { loc: 1, lat: 3 }. For mongoose you should provide such filter { 'geo.loc': 1, 'geo.lat': 3}.

As result better to define filter explicitly by your fields which you use for determining record uniqueness:

User.findOne({ isbn: args.record.isbn, 'geo.lat': args.record.geo.lat })

nodkz avatar Jan 19 '18 07:01 nodkz

Working like a charm! Thank you so much 🙏

mdegrees avatar Jan 19 '18 10:01 mdegrees

I wish I could say the same. I'm getting a nasty User.getResolver is not a function error in my console.

astrotars avatar Feb 12 '19 02:02 astrotars

@nparsons08 please open a new issue with your code. It's look like that you have syntax error or wrong imports.

nodkz avatar Feb 12 '19 03:02 nodkz