【问题标题】:How to simplify HTTP post of JSON to GraphQL mutation resolver如何简化 JSON 到 GraphQL 突变解析器的 HTTP 发布
【发布时间】:2019-12-27 16:36:05
【问题描述】:

我想将 HTTP POST 值直接作为 JSON 发送到我的 GraphQL Mutation 中已声明的 addBook 解析器。

但是,我看到(并证明)的示例使用从 JSON 到 SDL 的参数序列化或在 SDL 中重新声明变量以从查询变量绑定。

这两种方法都没有意义,因为 addBook 突变已经声明了所有参数和验证。使用这些方法会导致必须创建、调试和维护不必要的查询序列化逻辑。

我在浏览器中构建了格式良好的(模式编辑和验证)JSON,它符合声明的 GraphQLObjectType 的数据。

谁能解释在针对突变解析器发布时如何避免这种不必要的重新序列化或重复?

我一直在尝试将 JSON 数据结构映射到 addBook 突变的多种方法,但找不到简单发送 JSON 以便属性名称与 addBook 参数名称绑定的示例,而没有明显毫无意义的重新序列化或样板.

https://github.com/cefn/graphql-gist/tree/master/mutation-map 的源代码是一个可重现的最小示例,它演示了该问题。它有一个 addBook 解析器,它已经定义了参数名称、类型和可空性。我找不到一种方法来使用 JSON 来简单地针对 addBook 发布参数。

我使用 GraphiQL 作为 HTTP POST 值的参考实现。

我可以编写代码将 JSON 序列化为 SDL。它最终会看起来像这样,通过 GraphiQL 工作:

mutation {addBook(id:"4", name:"Education Course Guide", genre: "Education"){
    id
}}

或者,我可以编写代码将 addBook 的每个参数显式别名为不同的查询,然后允许我将值作为 JSON 查询变量发布,这也通过 GraphiQL 得到证明:

mutation doAdd($id: String, $name: String!, $genre: String){
  addBook(id:$id, name:$name, genre:$genre){
    id
  }
}

...使用查询变量...

{
  name: "Jonathan Livingstone Seagull",
  id: "6"
}

但是,我确信有某种方法可以直接针对 addBook 发布此 JSON,告诉它从查询变量中获取参数。我在想像……

mutation {addBook($*){
    id
}}

我希望对 addBook 的突变调用成功,从 JSON 查询变量中获取命名值,但不重新序列化或将属性重新声明为参数名称。

【问题讨论】:

    标签: json http post graphql mutation


    【解决方案1】:

    这归结为架构设计。而不是在你的领域有三个参数

    type Mutation {
      addBook(id: ID, name: String!, genre: String!): Book
    }
    

    你可以有一个单独的参数来接受输入对象类型

    type Mutation {
      addBook(input: AddBookInput!): Book
    }
    
    input AddBookInput {
      id: ID
      name: String!
      genre: String!
    }
    

    那么您的查询只需提供一个变量:

    mutation AddBook($input: AddBookInput!) {
      addBook(input: $input) {
        id
      }
    }
    

    你的变量看起来像:

    {
      "input": {
        "name": "Jonathan Livingstone Seagull",
        "genre": "Fable"
      }
    }
    

    变量必须明确定义为操作定义的一部分,因为 GraphQL 和 JSON 不可互换。 JSON 字符串值可以是 GraphQL 中的字符串、ID 或一些自定义标量(如 DateTime)。变量定义告诉 GraphQL 如何正确序列化和验证提供的 JSON 值。因为变量可以在整个文档中多次使用,所以它们的类型同样不能简单地从与它们一起使用的参数的类型中推断出来。

    编辑:

    每个文档只声明一次变量。一旦声明,它们可能会在整个文档中被多次引用。想象一下这样的查询

    mutation MyMutation ($id: ID!) {
      flagSomething(somethingId: $id)
      addPropertyToSomething(id: $id, property: "WOW")
    }
    

    我们声明该变量一次并告诉 GraphQL 它是一个 ID 标量并且它是不可为空的(即必需的)。然后我们使用该变量两次——一次作为flagSomething 上的somethingId 的值,再次作为addPropertyToSomething 上的id 的值。相同的变量也可以用作指令参数的值——它不仅限于字段参数。另请注意,没有任何内容表明变量名称必须与字段名称匹配——这通常只是为了方便。

    这里另一个值得注意的事情是这里发生了两个验证步骤。

    首先,GraphQL 将检查提供的变量(即 JSON 值)是否可以序列化为指定的类型。由于我们将变量声明为非空(使用!),GraphQL 还将验证变量是否确实存在且不等于空。

    GraphQL 还将验证您为变量指定的类型是否与实际使用它的参数的类型相匹配。因此,如果将 Int 变量传递给 String 参数等,则会抛出该变量。此外,这里也检查可空性。因此,一个为Int!(非空整数)的参数将只接受也是Int! 的变量。但是,Int(即可为空)的参数将接受 IntInt! 变量。

    存在的语法是有原因的。您想象的那种语法只有在您只查询单个根字段 and 使用所有变量作为该字段 and 的参数的特定场景中才有意义em> 变量名匹配参数名并且你不需要动态设置任何指令参数。

    【讨论】:

    • 谢谢,@DanielRearden。你的回答让我更接近。它简化了在没有命名参数的情况下如何映射 JSON。但是,它通过将 addBook 有效地重新声明为 AddBook,将 $input 重新声明为 $input。我认为 GraphQL 不应该强制嵌套重新声明,例如...为什么需要这个样板文件?没有其他建议,我会选择你的答案。它比重新声明每个参数更简单,更易于维护。
    • @cefn 对不起,也许我的回答不够清楚。没有发生任何 re 声明。请查看我的编辑。
    • 谢谢,@DanielRearden 你成功了。清楚地描述了为什么标准将 JSON 中的值显式绑定到查询范围(例如 MyMutation)中,从中调用解析器并在调用突变解析器定义时引用它(例如 flagSomething addPropertyToSomething)。解释我编写的任何“样板”实际上是如何工作的——允许来自 JSON 查询变量中(可能)多个不同名称条目的输入数据用于(可能)多个不同名称的解析器调用。我现在可以看到如何利用它了。谢谢!
    猜你喜欢
    • 2021-03-01
    • 2019-01-08
    • 2021-11-22
    • 1970-01-01
    • 2018-09-07
    • 2019-02-03
    • 2019-04-07
    • 2020-09-21
    • 1970-01-01
    相关资源
    最近更新 更多