如何修复 graphql 突变类型名称错误

how to fix graphql mutations typename errors

我尝试使用 apollo graphQl client 进行 GraphQl 突变。

当突变变量包含___typename属性(这显然不存在于graphQl模式中)时,这会产生error 500

为了修复可以在 graphQl 客户端配置中设置 addTypename: false 的问题:

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

现在突变几乎起作用了……

但是出现了新的错误: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.

那么应该如何配置 graphQl 客户端来处理突变?

现在我使用发现的清理函数 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;
};

但感觉很老套。是否有内置的 graphql 客户端?

获取某些查询的值然后将其作为变量插入是一种非典型情况,因此对于您尝试执行的操作并没有一个简单的解决方案。虽然您可以将客户端实例配置为从结果中省略 __typename 字段,但对象的 __typename(连同其 id_id)用作缓存键——如果它没有彻底破坏事物,省略它会导致一些意外行为。

到目前为止,最好的方法是在将查询结果作为变量传递之前对其进行操作。我认为这样的事情应该可以解决问题:

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

附带说明一下,除非您使用客户端数据(即 apollo-link-state),否则很难想象您从服务器获取一些数据然后必须提供相同数据的情况变成一个突变。如果数据已经存在于服务器上,则为其传递一个 id 并在服务器端检索它应该就足够了。如果您不得不跳过这些障碍,这可能表明 API 本身需要改变。

因此,感谢@Daniel Rearden 的想法和 this 评论,我使用自定义 apollo link 从突变变量中去除类型名称:

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

然后将它与其他 link 一起使用,如下所示:

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

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