React-apollo: Using query result in mutation passes through the __typename field

Created on 1 Jun 2017  路  19Comments  路  Source: apollographql/react-apollo

I have mutation like this:

async updateGroceryList(_, { input: { grocery_list } }, { user }) {
  return await Customer.findByIdUpdate(
    user.roles.customer_id,
    { grocery_list },
    { 'new': true }
  );
}

Mutation type:

updateGroceryList(input: updateGroceryListInput): Customer
input updateGroceryListInput {
  grocery_list: [groceryItem]
}
input groceryItem {
  item: String
  quantity: String
}

I am calling my mutation in React like so:

// fields: [{ item: 'hello', quantity: 'world' }, {...}, ...]

this.props.mutate({ variables: { input: { grocery_list: fields } } })
    .then(res => console.log(res))
    .catch(err => console.log(`error sending the query: ${err}`));

And I define the mutation in the component like this:

const mutation = gql`
  mutation updateGroceryList($input: updateGroceryListInput) {
    updateGroceryList(input: $input) {
      grocery_list {
        item
        quantity
      }
    }
  }
`;

export default graphql(mutation)(Grocery);

But when I send the mutation, this is my request payload:

mutation updateGroceryList($input: updateGroceryListInput) {
  updateGroceryList(input: $input) {
    grocery_list {
      item
      quantity
      __typename
    }
    __typename
  }
}

Which throws this error:

Variable "$input" got invalid value {"grocery_list":[{"item":"hello","quantity":"world"},{"item":"bye","quantity":"world","__typename":"Grocery"}]}.鈫礗n field "grocery_list": In element #1: In field "__typename": Unknown field.

Most helpful comment

Whit react-apollo 2.x the use is a little bit different

```
new ApolloClient({
link: new HttpLink({ uri: 'http://localhost:4000' }),
cache: new InMemoryCache({
addTypename: false
}),
});

All 19 comments

Yeah this is because the queries have __typename added to them to make data normalization work better, and when you pass that result through to the mutation it's still there.

Okay, that makes sense. How can I approach to solve this problem tho?
Should I add __typename when I define the input, like so?

input groceryItem {
  item: String
  quantity: String
  __typename: String
}

No I think the best workaround right now is to strip out the typename before passing the object into the mutation.

But the moment I call the mutation, there is nothing to strip out:

// fields: [{ item: 'hello', quantity: 'world' }, {...}, ...]

this.props.mutate({ variables: { input: { grocery_list: fields } } })
    .then(res => console.log(res))
    .catch(err => console.log(`error sending the query: ${err}`));

Are you sure each item in fields doesn't have __typename in it? Unless you've explicitly stripped them out from the query results, they'll be in there.

Okay, I guess I didn't do it correctly then. Will try again.
Thanks for the help, @aryo @stubailo !

Any solution for this other than the workaround suggested by @stubailo ?

@omt66 You can set up you ApolloClient to not add the __typename to your query results, like so:

new ApolloClient({
  addTypename: false,
  networkInterface: createNetworkInterface({ uri: '/graphql' })
});

Thanks, that's much better. I will give it a try.

Whit react-apollo 2.x the use is a little bit different

```
new ApolloClient({
link: new HttpLink({ uri: 'http://localhost:4000' }),
cache: new InMemoryCache({
addTypename: false
}),
});

Before adding addTypename: false to your cache initializer, be aware that this solution will prevent you resolving fragments on unions and interfaces, as described in the Apollo documentation for IntrospectionFragmentMatcher.

Looks like in a recent update warnings were added:
2017-12-13_17h31_17

So now this is becoming a real pain.

Why was this issue closed? Is this not a bug?

It would be nice to have a per-query option to strip out the __typenames. Something like:

import { graphql } from 'react-apollo';
import myQuery from './query.gql';

const MyComponent = () => { ... };

export default graphql(myQuery, {
  addTypename: false
});

This could potentially provide cleaner data results to this.props.data and still be compatible with resolving fragments/interfaces.

@JonathanWbn I was under the impression that fields not requested by the component are not passed into the component?

Seems like stripping __typename from all query results with addTypename: false is too broad of a solution that could cause unintended consequences like @mikefowler pointed out.

Another solution could be to use filter with a fragment using graphql-anywhere.

<VoteButtons
  entry={filter(VoteButtons.fragments.entry, entry)}
  canVote={loggedIn}
/>

Where the fragment does not have __typename listed as an attribute.

https://github.com/apollographql/apollo-client/blob/master/docs/source/features/fragments.md#filtering-with-fragments

You can also use this package: https://www.npmjs.com/package/typename-monkey-patch which filters out __typename fields without doing addTypename: false and use graphql HOC as usual.

You just require the module somewhere near the entry point of your app.

const { __typename, ...otherAttributes } = someQueryResultObject
mutate(otherAttributes)
const { __typename, ...otherAttributes } = someQueryResultObject
mutate(otherAttributes)

Thanks @speakingcode - that does the trick nicely.

const { __typename, ...otherAttributes } = someQueryResultObject
mutate(otherAttributes)

@speakingcode doesn't really work if you have __typenames nested in your response.

Was this page helpful?
0 / 5 - 0 ratings