It's 2019, and I am just getting started with GraphQL. Coming from a REST background, one intuitive pattern that I found myself implementing was:

  • Fetch data with Query
  • Perform updates on the data
  • Pass that data as input for a Mutation

However, when I tried that, I got stuck with an error:

"problems": [{
        "path": [
            "__typename"
        ],
        "explanation": "Field is not defined on ProductInput"
    }
]
Where did __typename come from? It wasn't in the Query response.

It turns out the Apollo Client library adds __typename field for its InMemoryCache. Apollo Client's issue #2160 is tracking the same issue, and the thread suggests removing this field either on a case-by-case basis or have it automatically removed with Apollo Link. I will show you how to do the latter.

Solution

This solution is based off some recommendations from Issue #2160.

Imports

import ApolloClient from "apollo-client";
import { createHttpLink } from "apollo-link-http";
import { InMemoryCache } from "apollo-cache-inmemory";
import { ApolloLink } from "apollo-link";
import { ApolloProvider } from "react-apollo";

Implement OmitDeep

const omitDeepArrayWalk = (arr, key) => {
  return arr.map(val => {
    if (Array.isArray(val)) return omitDeepArrayWalk(val, key);
    else if (typeof val === "object") return omitDeep(val, key);
    return val;
  });
};

const omitDeep = (obj, key) => {
  const keys = Object.keys(obj);
  const newObj = {};
  keys.forEach(i => {
    if (i !== key) {
      const val = obj[i];
      if (val instanceof Date) newObj[i] = val;
      else if (Array.isArray(val)) newObj[i] = omitDeepArrayWalk(val, key);
      else if (typeof val === "object" && val !== null)
        newObj[i] = omitDeep(val, key);
      else newObj[i] = val;
    }
  });
  return newObj;
};
const cleanTypenameLink = new ApolloLink((operation, forward) => {
  if (operation.variables && !operation.variables.file) {
    // eslint-disable-next-line
    operation.variables = omitDeep(operation.variables, "__typename");
  }

  return forward(operation);
});

Create an Apollo Client

const link = createHttpLink({
  uri: "/graphql"
});

const client = new ApolloClient({
  link: ApolloLink.from([
    cleanTypenameLink, link
  ]),
  cache: new InMemoryCache()
});

In case if you're using Apollo Boost, and want Boost's behavior, initialize Client as:

import { onError } from "apollo-link-error";

const link = createHttpLink({
  uri: "/graphql",
});

const client = new ApolloClient({
  link: ApolloLink.from([
    cleanTypenameLink,
    onError(({ graphQLErrors, networkError }) => {
      if (graphQLErrors)
        graphQLErrors.map(({ message, locations, path }) =>
          console.log(
            `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
          )
        );
      if (networkError) console.log(`[Network error]: ${networkError}`);
    }),
    link
  ]),
  cache: new InMemoryCache()
});

More about achieving Boost-like behavior using Apollo Client is documented here.