【问题标题】:how to fix graphql mutations typename errors如何修复 graphql 突变类型名错误
【发布时间】:2019-03-18 01:55:30
【问题描述】:

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

当变异变量包含___typename 属性时,这会生成error 500(这显然不存在于 graphQl 架构中)。

要解决可以在 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;
};

但感觉很hacky。有没有内置的graphql客户端?

【问题讨论】:

  • 您是否尝试将另一个查询返回的数据用作您的变异变量之一?否则,不清楚传递给突变的变量何时包含__typename 字段。
  • 是的。 ___typename 是从之前的查询中添加的

标签: javascript graphql graphql-js apollo-client


【解决方案1】:

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

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

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

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

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

【讨论】:

  • 谨慎使用这种方法,如果有任何不是 JS 原语的东西,如 File 或 Blob,将被删除(如强制转换为最接近的可字符串化值,如 {})并且永远不会作为 graphQl 调用的一部分发送,这对我来说是一个糟糕的下午的原因!
  • @MatteoHertel 如果您能分享您在不丢失文件或 Blob 数据的情况下删除类型名的方法,我将不胜感激。
  • @FaureHu 我在这里添加了一个完整版本github.com/apollographql/apollo-feature-requests/issues/… 打破这里的代码 `` function stripTypenames(obj: any, propToDelete: string) { for (const property in obj) { if (typeof obj[property] === 'object' && !(obj[property] instanceof File)) { delete obj.property; const newData = stripTypenames(obj[property], propToDelete); obj[属性] = newData; } else { if (property === propToDelete) { delete obj[property]; } } } 返回对象; } ```
【解决方案2】:

获取某个查询的值,然后将其作为变量插入突变是一种非典型场景,因此对于您要尝试做的事情并不容易解决。虽然您可以将客户端实例配置为从结果中省略 __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 本身需要更改。

【讨论】:

  • 感谢您的解释。但我不明白这是一个非典型场景:我从服务器获取article,用户修改文章中的一些字段,然后将article 作为突变发回。这里有什么不典型的?
  • 从概念上讲,查询返回的类型和用作参数的输入类型是完全不同的东西,即使它们作为 Javascript 对象共享一个或多个字段。就像您不能在模式中互换使用类型和输入类型一样,也不应该期望它们可以在客户端互换使用。
  • 想一想,您可以将上述逻辑封装在自定义 ApolloLink 中,以自动转换您发送的任何变量。这可能是要走的路。
  • 好的,非常感谢您的解释。我非常感谢自定义 apollo 链接封装的示例。
  • 哦!而且,您认为在获取对象之后立即删除___typename 更好,还是在发送突变之前将它们删除更好? (前者听起来更干净)
猜你喜欢
  • 1970-01-01
  • 2021-01-20
  • 2021-02-24
  • 2016-02-14
  • 2019-07-20
  • 1970-01-01
  • 2021-10-19
  • 1970-01-01
  • 2018-12-03
相关资源
最近更新 更多