Search code examples
reactjsgraphqlapolloapollo-clientapollo-boost

Apollo boost - __typename in query prevent new mutation


I have a problem in my meteor/react/apollo (with boost) project. When I query data from the server, it adds __typename to every object and subobject in my query but it my case it creates a major issue as I normally reuse these data to send them to other mutation. Now the other mutation tell me there is an error because the __typename field is not defined in my graphql schema.

I tried to fix by adding the addTypename: false field to my apollo client but it didn't change anything (note I am using apollo boost, that may be why it is not sworking) :

const client = new ApolloClient({
    uri: Meteor.absoluteUrl('graphql'),
    addTypename: false,
    request: operation =>
        operation.setContext(() => ({
            headers: {
                authorization: Accounts._storedLoginToken()
            }
        }))
})

Also it seems than even if it worked it is not very optimized. It seems to me very problematic that a field is added to the query results and I am surprised not to find any clear solution online. Some proposed solution where :

  • filter manually on client side
  • add middleware to apollo
  • add the __typename field to all my schemas...

but none of them seem to fit the 'simplcity' apollo is suppose to bring for queries. I hope there is a simpler, more logical solution provided, but so far, could not find any.


Solution

  • Even if using apollo-client and not apollo-boost, you shouldn't set addTypename to false unless you have a compelling reason to do so. The __typename field is used by the InMemoryCache to normalize your query results, so omitting it will likely lead to unexpected behavior around caching.

    Unfortunately, there is no "silver bullet" to this problem. Requesting a query and then using that query's data as the variable to some other query could be construed as misusing the API. The Type returned by a query and the Input Type used as an argument are completely different things, even if as Javascript objects they share one or more fields. Just like you can't use types and input types interchangeably within a schema, there shouldn't be an expectation that they can be used interchangeably client-side.

    That also means that if you're finding yourself in this situation, you may want to take a second look at your schema design. After all, if the data exists on the server already, it should be sufficient to pass in an id for it and retrieve it server-side, and not have to pass in the entire object.

    If you're using some query to populate one or more inputs and then using the value of those inputs inside a mutation, then you're presumably already turning the initial query data into component state and then using that in your mutation. In that scenario, __typename or any other non-editable fields probably shouldn't be included as part of component state in the first place.

    At the end of day, doing these sort of manipulations will hopefully be the exception, and not the rule. I would create some kind of helper function to "sanitize" your input and move on.

    function stripTypenames (value) {
        if (Array.isArray(value)) {
            return value.map(stripTypenames)
        } else if (value !== null && typeof(value) === "object") {
          const newObject = {}
          for (const property in value) {
              if (property !== '__typename') {
                newObject[property] = stripTypenames(value[property])
              }
          }
          return newObject
        } else {
          return value
        }
    }