GraphQL ObjectType с динамическими полями на основе аргументов

мы находимся в ситуации, когда ответ нашего запроса 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) возврат a "Любой-Объект"!--13-->

мы могли бы просто вернуть любой объект-поэтому 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),
        },
      },
    },
    // ...
  },
});

Это может сработать, но вопрос будет в том, есть ли способ передать args и/или разрешить объект в поля.

вопрос Так теперь наш вопрос: какой путь будет рекомендован в нашем случае в GraphQL и возможно ли решение 1 или 2 ? Может есть другое решение ?

редактировать Решение 1 будет работать при использовании ScalarType. Пример:

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

Я не уверен, что это рекомендуемый способ решить нашу ситуацию.

1 ответов


ни один из вариантов не является действительно жизнеспособным:

  1. GraphQL строго типизирован. GraphQL.JS не поддерживает какой-то any поле, и все типы, определенные в вашей схеме, должны иметь определенные поля. если вы посмотрите в документации, fields является обязательным -- если вы попытаетесь оставить его, вы нажмете ошибку.

  2. Args используются для разрешения запросов на основе каждого запроса. Вы не сможете вернуть их в свою схему. Вы схема должно быть статично.

как вы предполагаете, можно выполнить то, что вы пытаетесь сделать, свернув свой собственный скаляр клиента. Я думаю, что более простым решением было бы просто использовать JSON -- вы можете импортировать пользовательский скаляр для него, как этот. Тогда просто elements разрешение поля для объекта JSON или массива, содержащего динамические поля. Вы также можете управлять объектом JSON внутри распознавателя на основе аргументов, если это необходимо (если вы хотите например, чтобы ограничить поля, возвращаемые подмножеству, как определено в args).

предупреждение: проблема с использованием JSON или любого пользовательского скаляра, который включает вложенные данные, заключается в том, что вы ограничиваете гибкость клиента в запросе того, что ему действительно нужно. Это также приводит к менее полезным ошибкам на стороне клиента - я бы предпочел, чтобы мне сказали, что поле, которое я запросил, не существует или вернуло null когда я делаю запрос чем узнать позже по линии JSON blob, который я получил, не включал поле, которое я ожидал.