Як запитувати всі поля типу GraphQL, не записуючи довгий запит?


130

Припустимо, у вас тип GraphQL, і він включає багато полів. Як здійснити запит на всі поля, не записуючи довгий запит, що включає назви всіх полів?

Наприклад, якщо у мене є ці поля:

 public function fields()
    {
        return [
            'id' => [
                'type' => Type::nonNull(Type::string()),
                'description' => 'The id of the user'
            ],
            'username' => [
                'type' => Type::string(),
                'description' => 'The email of user'
            ], 
             'count' => [
                'type' => Type::int(),
                'description' => 'login count for the user'
            ]

        ];
    }

Для запиту всіх полів зазвичай запит виглядає приблизно так:

FetchUsers{users(id:"2"){id,username,count}}

Але я хочу, щоб такі самі результати були без написання всіх полів, приблизно так:

FetchUsers{users(id:"2"){*}}
//or
FetchUsers{users(id:"2")}

Чи є спосіб це зробити в GraphQL ??

Я використовую бібліотеку Folkloreatelier / laravel-graphql .


4
Ви запитуєте, як зробити те, що GraphQL, за задумом, не підтримує.
Тревіс Вебб

12
Просто введіть ці 40-ти полів і сподіваємось, що ви не зробите помилку на помилку :)
Ска,

32
Нічого собі, я тільки починаю з GraphQL, і це серйозна WTF.
user949300

1
Має сенс, що це не підтримується, уявіть, що у вас є об'єкти "Студент і клас", у студентів є польові "класи", в яких перераховані всі класи, які він відвідує, у класі є поле "студенти", в якому перераховані всі учні, які відвідують цей клас. Ось циклічна структура. Тепер, якщо ви запитуєте всіх студентів із усіма полями, чи буде це також включати всі повернуті поля класів? І в цих класах є студенти, чи були б також включені їхні поля? І студенти мають заняття, ...
Buksy

У мене виникло це питання, і це було так, що я міг бачити, що навіть можна було тягнути. У багатьох клієнтів GraphQL (наприклад, GraphiQL, див. Gatsbyjs.org/docs/running-queries-with-graphiql ) є програма-програма для перевірки схеми, яка використовує інтроспекцію, щоб представити вам те, що ви можете витягнути, якщо це причина, чому ви хочете отримати "все ".
Джеймс

Відповіді:


120

На жаль, те, що ви хотіли б зробити, неможливо. GraphQL вимагає від вас чітко вказати, які поля ви хотіли б повернути зі свого запиту.


5
Гаразд, і якщо я запитую якийсь об'єкт невідомої форми з бекенда, який я повинен проксі або відправити назад?
meandre

19
@meandre, вся ідея graphql полягає в тому, що не існує такого поняття, як "невідома форма".
s.meijer

2
@meandre, моя відповідь нижче може бути корисною для вас?
Тайрон Вілсон

Хіба це не вся ідея більшості мов API запитів і протоколів?, @Meandre
Клійстерс

Що цікаво, це дійсно інший спосіб мислення при використанні graphql
Andy mccullough

91

Так, це можна зробити за допомогою самоаналізу . Зробіть запит GraphQL типу (для типу UserType )

{
   __type(name:"UserType") {
      fields {
         name
         description
      }  
   }
}

і ви отримаєте відповідь типу (фактичні назви полів залежатимуть від вашої фактичної схеми / визначення типу)

{
  "data": {
    "__type": {
      "fields": [
        {
          "name": "id",
          "description": ""
        },
        {
          "name": "username",
          "description": "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only."
        },
        {
          "name": "firstName",
          "description": ""
        },
        {
          "name": "lastName",
          "description": ""
        },
        {
         "name": "email",
          "description": ""
        },
        ( etc. etc. ...)
      ]
    }
  }
}

Потім можна прочитати цей список полів у вашому клієнті та динамічно скласти другий запит GraphQL, щоб отримати всі ці поля.

Це залежить від того, що ви знаєте назву типу, для якого ви хочете отримати поля - якщо ви не знаєте типу, ви можете зібрати всі типи та поля разом, використовуючи самоаналіз, як

{
  __schema {
    types {
      name
      fields {
        name
        description
      }
    }
  }
}

ПРИМІТКА: це надмісні дані GraphQL - ви самостійно розбираєтесь, як читати та писати разом із фактичним клієнтом. Ваша бібліотека JavaScriptQL JavaScript вже може використовувати інтроспекцію в певній якості, наприклад, команда apollo codegen використовує інтроспекцію для генерування типів.


Схоже, що слід виражати турботу про рекурсивні типи. Якщо ви зійшли з дерева і натрапили на тип, який містить себе, в якійсь формі (список, одинарний чи інший ..), ви могли б отримати нескінченну рекурсію.
Мілош Груїч

Це, власне, не трапляється в моєму досвіді з цим запитом - сам запит визначає глибину роздільної здатності.
Марк Чакерян

Наведена вище відповідь дозволяє лише запитувати типи полів, доступних у запиті. Він не повертає всі об’єктні поля "значень", про що йдеться в оригінальному питанні.
Quantdaddy

4
Відповідно до відповіді, ви повинні динамічно будувати другий запит на основі результатів першого запиту - я залишив це як вправу для читача.
Марк Чакерян

39

Я думаю, що єдиний спосіб зробити це - використовуючи фрагменти багаторазового використання:

fragment UserFragment on Users {
    id
    username
    count
} 

FetchUsers {
    users(id: "2") {
        ...UserFragment
    }
}

19
Якщо я це зробив, то все-таки я мушу писати кожне ім’я поля "принаймні у фрагменті", відмовляючись від того, чого я намагався уникати, здається, що GraphQL змушує нас бути явними.
BlackSigma

як додати це у запиті POSTMan? або фреймворк jquery / UI, щоб створити структурований JSON. Цей graphiQL видається марним для фактичних цілей розробки.
mfaisalhyder

Це виключно для повторного використання.
Хенок Тесфайе

@BlackSigma Враховуючи документацію GraphQL , це має бути найкращою відповіддю
JP Ventura

4
@JPVentura: Ні, мій друг, є різниця між багаторазовим використанням та символом, як у концепції, так і в застосуванні. Призначення фрагмента зрозуміло в документації "GraphQL включає одиниці багаторазового використання, які називаються фрагментами". Використання фрагмента корисно, але не є відповіддю на запитання.
BlackSigma

11

З цією ж проблемою я зіткнувся, коли мені потрібно було завантажити дані про місцезнаходження, які я серіалізував у базу даних через API google places. Як правило, я хотів би, щоб все це працювало з картами, але я не хотів кожного разу вказувати всі поля.

Я працював у Ruby, тому я не можу дати вам реалізацію PHP, але принцип повинен бути таким же.

Я визначив користувацький скалярний тип під назвою JSON, який просто повертає буквальний об'єкт JSON.

Реалізація рубіну виглядала так (використовуючи graphql-ruby)

module Graph
  module Types
    JsonType = GraphQL::ScalarType.define do
      name "JSON"
      coerce_input -> (x) { x }
      coerce_result -> (x) { x }
    end
  end
end

Тоді я використовував це для наших об’єктів так

field :location, Types::JsonType

Я б використовував це дуже ощадно, але використовуючи його лише там, де ви знаєте, що вам завжди потрібен весь об’єкт JSON (як це було у моєму випадку). В іншому випадку він перемагає об'єкт GraphQL в цілому.


1
Це саме те, що мені було потрібно, дякую. Мій випадок використання - це те, що у мене в системі є рядки, що перекладаються користувачем, і вони зберігаються як json в db, як {"en": "Hello", "es": "Hola"}. Оскільки кожен користувач може реалізувати власний підмножина мов для випадку їх використання, користувальницький інтерфейс не має сенсу запитувати всі можливі підмножини. Ваш приклад прекрасно працює.
Лука Ересман

2

Формат запиту GraphQL був розроблений для того, щоб:

  1. Як запит, так і форма результату точно однакові .
  2. Сервер точно знає запитувані поля, тому клієнт завантажує лише необхідні дані.

Однак, згідно з документацією GraphQL , ви можете створювати фрагменти , щоб зробити набори вибору більш багаторазовими:

# Only most used selection properties

fragment UserDetails on User {
    id,
    username
} 

Тоді ви можете запитати всі деталі користувача:

FetchUsers {
    users() {
        ...UserDetails
    }
}

Ви також можете додати додаткові поля поряд із фрагментом :

FetchUserById($id: ID!) {
    users(id: $id) {
        ...UserDetails
        count
    }
}

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.