Тип поля Apollo/GraphQL для объекта с динамическими клавишами

Скажем, мой сервер graphql хочет получить следующие данные в виде JSON, где person3 и person5 - некоторые id:

"persons": {
  "person3": {
    "id": "person3",
    "name": "Mike"
  },
  "person5": {
    "id": "person5",
    "name": "Lisa"
  }
}

Вопрос: Как создать определение типа схемы с помощью apollo?

Ключи person3 и person5 здесь динамически генерируются в зависимости от моего запроса (т. person5 area используемой в запросе). Поэтому в другой раз я могу вернуть person1, person2, person3. Как вы видите, persons не являются Iterable, поэтому следующее не будет работать как определение типа graphql, которое я сделал с apollo:

type Person {
  id: String
  name: String
}
type Query {
  persons(area: String): [Person]
}

Ключи в объекте persons всегда могут быть разными.

Одним из решений, конечно, было бы преобразование входящих данных JSON для использования массива для persons, но разве нет возможности работать с данными как таковыми?

Ответ 1

GraphQL полагается как на сервер, так и на клиента, заранее зная, какие поля доступны для каждого типа. В некоторых случаях клиент может обнаружить эти поля (посредством интроспекции), но для сервера они всегда должны быть известны заранее. Таким образом, каким-то образом динамически генерировать эти поля на основе возвращенных данных не представляется возможным.

Вы можете использовать собственный скаляр JSON (модуль graphql-type-json) и вернуть его для своего запроса:

type Query {
  persons(area: String): JSON
}

Используя JSON, вы обходите требование, чтобы возвращаемые данные соответствовали какой-либо конкретной структуре, поэтому вы можете отправлять обратно все, что хотите, до тех пор, пока JSON будет правильно отформатирован.

Конечно, в этом есть существенные недостатки. Например, вы теряете сеть безопасности, обеспечиваемую типом (ами), которые вы ранее использовали (буквально любая структура может быть возвращена, и если вы возвращаете неправильную, вы не узнаете об этом, пока клиент не попытается) использовать его и не удается). Вы также теряете возможность использовать средства распознавания для любых полей возвращаемых данных.

Но... твои похороны :)

Кроме того, я хотел бы рассмотреть вопрос о сведении данных в массив (как вы предложили в своем вопросе) перед отправкой их обратно клиенту. Если вы пишете клиентский код и работаете со списком клиентов с динамическим размером, скорее всего, с массивом будет работать гораздо легче, чем с объектом, идентифицированным по id. Если вы, например, используете React и отображаете компонент для каждого клиента, вы в конечном итоге преобразуете этот объект в массив, чтобы все равно отобразить его. При разработке вашего API, я бы сделал так, чтобы удобство использования клиента было более важным, чем избежание дополнительной обработки ваших данных.

Ответ 2

Вы можете написать свой собственный GraphQLScalarType и точно охарактеризовать свой объект и ваши динамические клавиши, то, что вы разрешаете, и то, что вы не разрешаете или не трансформируете.

См. Https://graphql.org/graphql-js/type/#graphqlscalartype.

Вы можете посмотреть на taion/graphql-type-json, где он создает Scalar, который позволяет и преобразует любой контент:

https://github.com/taion/graphql-type-json/blob/master/src/index.js