4
votes

I try to make a GraphQl mutation using apollo graphQl client.

This makes an error 500 when the mutation variables contains ___typename properties (which obviously don't exist in the graphQl schema).

To fix that it is possible to set addTypename: false in the graphQl client config:

const graphqlClient = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache({
    addTypename: false
  })
})

Now the mutation almost works…

But there is a new error: You're using fragments in your queries, but either don't have the addTypename: true option set in Apollo Client, or you are trying to write a fragment to the store without the __typename. Please turn on the addTypename option and include __typename when writing fragments so that Apollo Client can accurately match fragments.

So how should the graphQl client be configured to handle mutations?

for now I use a cleanup function found here:

const removeTypename = (value) => {
  if (value === null || value === undefined) {
    return value;
  } else if (Array.isArray(value)) {
    return value.map(v => removeTypename(v));
  } else if (typeof value === 'object') {
    const newObj = {};
    Object.keys(value).forEach(key => {
      if (key !== '__typename') {
        newObj[key] = removeTypename(value[key]);
      }
    });
    return newObj;
  }
  return value;
};

but it feels hacky. Is there something built-in graphql client?

2
Are you trying to use the data returned by another query as one of your mutation's variables? Otherwise, it's not clear when the variables passed to a mutation would contain a __typename field.Daniel Rearden
yes exactly. The ___typename were added from a previous queryFrançois Romain

2 Answers

4
votes

So, thanks to @Daniel Rearden idea and this comment, I use a custom apollo link to strip typenames from mutations variables:

const omitTypename = (key, value) => {
  return key === '__typename' ? undefined : value
}

const omitTypenameLink = new ApolloLink((operation, forward) => {
  if (operation.variables) {
    operation.variables = JSON.parse(
      JSON.stringify(operation.variables),
      omitTypename
    )
  }
  return forward(operation)
})

Then use it with the other links like so:

const link = ApolloLink.from([authLink, omitTypenameLink, httpLink])
const cache = new InMemoryCache()

const graphqlClient = new ApolloClient({
  link,
  cache
})
1
votes

Taking the value of some query and then plugging it mutation as a variable is an atypical scenario, so there's not an easy solve for what you're trying to do. While you can configure your client instance to omit the __typename field from your results, an object's __typename (along with its id or _id) are used as cache keys -- omitting it is going to cause some unexpected behavior if it doesn't outright break things.

By far the best approach to this is just to manipulate the query result before passing it in as a variable. I think something like this should do the trick:

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
    }
}

On a side note, unless you're using client-side data (i.e. apollo-link-state) it's hard to image a situation where you would fetch some data from the server and then have to feed that same data into a mutation. If the data exists on the server already, it should be sufficient to pass in an id for it and retrieve it server-side. If you're having to jump through these sort of hoops, it may be an indicator that the API itself needs to change.