GraphQL ObjectType,包含基于参数的动态字段

TJR*_*TJR 13 javascript graphql

我们的情况是GraphQL Query的响应必须返回对象的一些动态属性.在我们的例子中,我们无法预定义所有可能的属性 - 因此它必须是动态的.

我们认为有两种方法可以解决它.

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

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

{
  name: 'some name',
  elements: {
    an_unkonwn_key: {
      some_nested_field: {
        some_other: true,
      },
    },
    another_unknown_prop: 'foo',
  },
}
Run Code Online (Sandbox Code Playgroud)

1)返回"任意对象"

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

我们已经改变了这个:

elements: {
      type: new GraphQLObjectType({ name: 'elements' });
    },
Run Code Online (Sandbox Code Playgroud)

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),
        },
      },
    },
    // ...
  },
});
Run Code Online (Sandbox Code Playgroud)

这可能有效,但问题是如果有办法将args和/或解析对象传递给字段.

问题 所以我们现在的问题是:在GraphQL中我们建议使用哪种方式,是否可以使用解决方案1或2?也许有另一种解决方案?

使用ScalarType时,编辑解决方案1将起作用.例:

type: new GraphQLScalarType({
        name: 'elements',
        serialize(value) {
          return value;
        },
      }),
Run Code Online (Sandbox Code Playgroud)

我不确定这是否是一种解决我们情况的推荐方法.

Dan*_*den 13

这两种选择都不可行:

  1. GraphQL是强类型的.GraphQL.js不支持某种any字段,并且架构中定义的所有类型都必须定义字段.如果您查看文档,fields则是必需的 - 如果您尝试将其删除,则会出现错误.

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

正如您所建议的那样,通过滚动您自己的客户Scalar可以实现您想要做的事情.我认为一个更简单的解决方案就是使用JSON - 你可以像这样导入一个自定义标量.然后让您的elements字段解析为包含动态字段的JSON对象或数组.如果需要,您还可以根据参数操作解析程序内的JSON对象(例如,如果要将返回的字段限制为args中定义的子集).

警告:利用JSON或包含嵌套数据的任何自定义标量的问题在于,限制了客户端在请求实际需要时的灵活性.它还会导致客户端上的错误更少 - 我更倾向于被告知我请求的字段不存在或在我发出请求时返回null 不是稍后找到我获得的JSON blob没有包括我期望的领域.

  • 您的回答听起来不错。我们将按照您的答案尝试定义的JSON类型。当然,当我们仅返回“任何对象”而无法控制其急需的数据时,灵活性将受到限制。但是在特殊情况下,我们没有其他选择,因为该属性在我们的graphql服务器上是未知的。但是:在我们的逻辑中,这种情况非常特殊且独特。 (2认同)
  • 如何使用JSON.stringify()将任何json obj制成字符串,然后将该字符串提供给键,例如{any_data:'stringified json object'} (2认同)

Poi*_*rks 5

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

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