mattkrick / cashay

:moneybag: Relay for the rest of us :moneybag:
MIT License
453 stars 28 forks source link

mutation subfields when no mutationHandlers seen yet #152

Closed dustinfarris closed 7 years ago

dustinfarris commented 7 years ago

Scenario:

Is it possible to have Cashay ask for a minimum of an "id" or something?

jordanh commented 7 years ago

Yes you can! This is a pattern we use rather regularly with Cashay/GraphQL in our application Action.

What we do is use Cashay's @cached operator to query locally. Cashay will return an object along the shape of our requested query. If it's the first time the route is accessed, the query will return with the proper shape but with empty data. When the mutation fires, the mutation handler can return the data for the query, and the @cached query will then return the subfields as requested.

For some example, see how admins can login as other users or how we authenticate and update user preferences.

dustinfarris commented 7 years ago

@jordanh Interesting! @cached looks really neat. I'll have to play with this to figure out how to make it work for me.

Thanks for open-sourcing action. That project has exponentially increased my GraphQL on-boarding velocity—truly grateful.

dustinfarris commented 7 years ago

@jordanh this is working for me! (in Ember, but should look similar):

// looking for an item that doesn't exist yet
const newItemQuery = `
{
  item @cached(type: "Item") {
    id
    name
  }
}
`;

const stateToComputed = () => {

  const { data: { item } } = cashay.query(newItemQuery, {
    op: 'createItem',
    mutationHandlers: {
      // Registering so that Cashay selects an id
      createItem() { }
    },
    resolveCached: {
      // We don't actually care about this yet
      item: () => { }
    },
  });

  return { item };
};

const CreateItem = Component.extend({

  createItem: task(function *(changeset) {

    yield cashay.mutate('createItem', {
      variables: {
        // `changest.change` will be something like:  `{ name: 'My New Item' }`
        item_details: changeset.get('change')
      }
    }).then(response => {  // waiting for an id...
      if (!response.error) {
        // "itemCreated" is an upstream action that will redirect to a the detail page
        this.sendAction('itemCreated', response.data.createItem.id);
      }
    });

  }).drop(),  // drop prevents this task from being called more than once

  layout: hbs`

    <!-- This is really cool because I can pass the empty "item" object from
         the cached query to the form component! -->

    {{create-map-form
      changeset=(changeset item)
      submit=(perform createItem)
    }}

  `
});

export default connect(stateToComputed)(CreateItem);

What I think might be possible, is to actually create the item in the background with empty data, then when the user clicks "Create" what's really happening is an update. Then the user doesn't have to wait for an id to be redirected because it's already there. Many ways to skin a cat here I'm sure.

jordanh commented 7 years ago

This looks awesome! Nice work @dustinfarris!