【问题标题】:GraphQL ObjectType with dynamic fields based on arguments具有基于参数的动态字段的 GraphQL ObjectType
【发布时间】:2018-02-01 05:03:02
【问题描述】:

我们的 GraphQL 查询的响应必须返回对象的一些动态属性。在我们的例子中,我们无法预定义所有可能的属性 - 所以它必须是动态的。

我们认为有两种解决方案。

const MyType = new GraphQLObjectType({
  name: 'SomeType',
  fields: {
    name: {
      type: GraphQLString,
    },
    elements: {
      /*
      THIS is our special field which needs to return a dynamic object 
      */
    },
    // ...
  },
});

正如您在示例代码中看到的,元素是必须返回对象的属性。解决此问题时的响应可能是:

{
  name: 'some name',
  elements: {
    an_unkonwn_key: {
      some_nested_field: {
        some_other: true,
      },
    },
    another_unknown_prop: 'foo',
  },
}

1) 返回“任意对象”

我们可以只返回任何对象——所以 GraphQL 不需要知道对象有哪些字段。当我们告诉 GraphQL 该字段是 GraphQlObjectType 类型时,它需要定义字段。因此,似乎不可能告诉 GraphQL 某人只是一个对象。

为此,我们已将其更改为:

elements: {
      type: new GraphQLObjectType({ name: 'elements' });
    },

2) 我们可以定义动态字段属性,因为它在函数中

当我们将字段定义为一个函数时,我们可以动态地定义我们的对象。但是字段函数需要一些信息(在我们的例子中是传递给元素的信息),我们需要访问它们来构建字段对象。

例子:

const MyType = new GraphQLObjectType({
  name: 'SomeType',
  fields: {
    name: {
      type: GraphQLString,
    },
    elements: {
      type: new GraphQLObjectType({
        name: 'elements',
        fields: (argsFromElements) => {
          // here we can now access keys from "args"
          const fields = {};
          argsFromElements.keys.forEach((key) => {
            // some logic here ..
            fields[someGeneratedProperty] = someGeneratedGraphQLType;
          });
          return fields;
        },
      }),
      args: {
        keys: {
          type: new GraphQLList(GraphQLString),
        },
      },
    },
    // ...
  },
});

这可行,但问题是是否有办法将参数和/或解析对象传递给字段。

问题 所以我们现在的问题是:在我们的 GraphQL 案例中推荐哪种方式,解决方案 1 或 2 可能吗?也许还有其他解决方案?

编辑 解决方案 1 在使用 ScalarType 时会起作用。示例:

type: new GraphQLScalarType({
        name: 'elements',
        serialize(value) {
          return value;
        },
      }),

我不确定这是否是解决我们问题的推荐方法。

【问题讨论】:

    标签: javascript graphql


    【解决方案1】:

    这两种选择都不可行:

    1. GraphQL 是强类型的。 GraphQL.js 不支持某种any 字段,并且在您的架构中定义的所有类型都必须定义字段。 If you look in the docsfields 是必需的——如果你试图忽略它,你会遇到错误。

    2. Args 用于基于每个请求解析查询。您无法将它们传递回您的架构。您的架构应该是静态的。

    正如您所建议的,您可以通过滚动您自己的客户标量来完成您想要做的事情。我认为一个更简单的解决方案是只使用 JSON——你可以导入一个custom scalar for it like this one。然后让您的 elements 字段解析为包含动态字段的 JSON 对象或数组。如有必要,您还可以根据参数在解析器中操作 JSON 对象(例如,如果您想将返回的字段限制为 args 中定义的子集)。

    警告词:使用 JSON 或任何包含嵌套数据的自定义标量的问题在于,您限制了客户端请求其实际需要的灵活性。它还会导致客户端出现不太有用的错误——我宁愿被告知我请求的字段不存在或返回 null 当我提出请求时,而不是稍后找出我得到的 JSON blob 行没有包含我期望的字段。

    【讨论】:

    • 你的回答听起来不错。我们将尝试在您的答案中定义的 JSON 类型。当然,当我们只返回“任何对象”而无法控制其需要的数据时,灵活性将受到限制。但在我们的特殊情况下,我们没有其他选择,因为我们的 graphql 服务器上的属性是未知的。但是:这个案例在我们的逻辑中非常特殊和独特。
    • 如何使用 JSON.stringify() 将任何 json obj 转换为字符串,然后将字符串传递给键,例如{any_data: '字符串化 json 对象'}
    • @NicolasS.Xu 这是个好主意!我有同样的问题,但通过将我的数据转换为后端的字符串然后使用正则表达式在客户端解析它来解决它。
    • @ToriHuang 正则表达式?你为什么不直接做JSON.parse(string)
    【解决方案2】:

    另一种可能的解决方案是将任何此类动态对象声明为字符串。然后将对象的字符串化版本作为值从您的解析器函数传递给该对象。然后最终您可以再次将该字符串解析为 JSON,使其再次成为客户端的对象。

    我不确定它是否推荐的方式,但我尝试使它与这种方法一起工作并且它运行顺利,所以我在这里分享它。

    【讨论】:

    • 好聪明的方法!谢谢!
    猜你喜欢
    • 2020-01-30
    • 2017-03-04
    • 2017-01-02
    • 1970-01-01
    • 2020-09-16
    相关资源
    最近更新 更多