【问题标题】:Remove read-only fields before mutation in GraphQL在 GraphQL 中的突变前删除不必要的字段
【发布时间】:2017-07-26 16:06:15
【问题描述】:

我的架构中有一个名为 Article 的类型:

type Article {
  id: ID!
  updated: DateTime
  headline: String
  subline: String
}

对于它的更新,updateArticle(id: ID!, article: ArticleInput!) 突变使用了相应的输入类型:

input ArticleInput {
  headline: String
  subline: String
}

突变本身如下所示:

mutation updateArticle($id: ID!, $article: ArticleInput!) {
  updateArticle(id: $id, article: $article) {
    id
    updated
    headline
    subline
  }
}

文章总是作为一个整体保存(而不是一个一个字段),所以当我将一篇文章传递给我之前获取的那个突变时,它会抛出像 Unknown field. In field "updated"Unknown field. In field "__typename" 和 @987654329 这样的错误@。这些有根本原因,这些字段没有在输入类型上定义。

根据spec,这是正确的行为:

(...) 这个无序映射不应该包含任何名称不是 由该输入对象类型的字段定义,否则出错 应该扔掉。

现在我的问题是处理这些情况的好方法是什么。我应该在我的应用代码中列出输入类型允许的所有属性吗?

如果可能的话,我想避免这种情况,并且可能有一个实用函数为我切掉它们,它知道输入类型。但是,由于客户端不知道架构,这必须发生在服务器端。因此,不必要的属性将被转移到那里,我想这就是为什么它们不应该首先被转移的原因。

有没有比维护属性列表更好的方法?

我正在使用apollo-clientreact-apollographql-server-express

【问题讨论】:

  • 你能显示你正在调用的实际突变吗?听起来这里有些不对劲。
  • 感谢@marktani 的快速回复!我已将突变代码添加到问题中。
  • 您是否有可能对您的服务器运行“原始”突变?也就是说,使用 GraphiQL 或 HTTP 客户端,如 curl 或 fetch。我想了解这是 Apollo 抛出的错误还是您的服务器抛出的错误。
  • 我刚刚用 curl 模拟了请求。该错误肯定是由服务器抛出的。删除输入类型未知的字段可以解决问题。
  • 没错,根本原因似乎是突变不知道这些领域。我首选的解决方案是忽略它们,但这将涉及将它们传输到服务器,我想这是不支持的原因。遗憾的是,我无法与您共享端点,但我猜当使用包含额外字段的输入类型调用突变时,每个 GraphQL 端点都会以相同的方式响应。

标签: javascript graphql graphql-js react-apollo apollo-client


【解决方案1】:

所以我能想到的最优雅的方式是使用片段进行查询,其中包括数据的所有可变字段。 graphql-anywherefilter utility 可以使用该片段在突变发生之前删除所有不需要的数据。

要点是:

const ArticleMutableFragment = gql`
fragment ArticleMutable on Article {
  headline
  subline
  publishing {
    published
    time
  }
}
`

const ArticleFragment = gql`
fragment Article on Article {
  ...ArticleMutable
  id
  created
  updated
}
${ArticleMutableFragment}
`;

const query = gql`
query Article($id: ID!) {
  article(id: $id) {
    ...Article
  }
}
${ArticleFragment}
`;

const articleUpdateMutation = gql`
mutation updateArticle($id: ID!, $article: ArticleInput!) {
  updateArticle(id: $id, article: $article) {
    ...Article
  }
}
${ArticleFragment}
`;

...

import {filter} from 'graphql-anywhere';

...

graphql(articleUpdateMutation, {
  props: ({mutate}) => ({
    onArticleUpdate: (id, article) =>
      // Filter for properties the input type knows about
      mutate({variables: {id, article: filter(ArticleMutableFragment, article)}})
  })
})

...

ArticleMutable 片段现在也可以用于创建新文章。

【讨论】:

  • 是的,GraphQL 目前不包括很多用于 CRUD 操作的好东西,例如通过输入对象保存整个对象的实用程序。它有点假设您使用您要传入的数据来调用每个突变。这可能很好,因为如果有人向输入类型添加了一个新字段,那么您最终会发送比您预期更多的字段。
  • 确实如此。另一方面,当将新字段添加到Article 并使用视图进行查询和更新时,我可能会忘记将它们添加到白名单中——所以要记住这一点。但很高兴知道我没有遗漏任何明显的东西——谢谢!
  • 实际上,这可以通过graphql-anywhere 包的过滤功能很好地解决。这样我就可以编写一个查询,在突变之前过滤数据。该查询可以放在获取数据的查询旁边,这样可能更容易保持同步。
  • 或者甚至更好的是查询和过滤查询之间的共享片段,用于突变。我再试一次。
  • 哦,我非常喜欢这个,因为它仍然可以让您准确指定要发送的字段!
【解决方案2】:

我个人也有同样的想法,并在早些时候采用了@amann 的方法,但一段时间后,在输入类型上使用查询片段的概念缺陷变得明显。您将无法选择(相应的)对象类型中不存在的输入类型字段 - 甚至有吗?

目前我通过typesafe-joi 模式描述我的输入数据,并使用它的stripUnknown 选项过滤掉我的表单数据。

无效数据永远不会离开表单,因此可以静态输入有效数据。

从某种意义上说,创建 joi 架构与定义“输入片段”的活动相同,因此不会发生代码重复,并且您的代码可以是类型安全的。

【讨论】:

    猜你喜欢
    • 2019-08-21
    • 1970-01-01
    • 2019-08-11
    • 1970-01-01
    • 2019-01-31
    • 2021-09-17
    • 2016-09-29
    • 2015-04-22
    • 1970-01-01
    相关资源
    最近更新 更多