Comment interroger tous les champs de type GraphQL sans écrire une longue requête?

130

Supposons que vous ayez un type GraphQL et qu'il inclut de nombreux champs. Comment interroger tous les champs sans écrire une longue requête qui inclut les noms de tous les champs?

Par exemple, si j'ai ces champs:

 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'
            ]

        ];
    }

Pour interroger tous les champs, la requête est généralement quelque chose comme ceci:

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

Mais je veux un moyen d'avoir les mêmes résultats sans écrire tous les champs, quelque chose comme ceci:

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

Y a-t-il un moyen de faire cela dans GraphQL ??

J'utilise Folkloreatelier / Laravel-graphql bibliothèque.

BlackSigma
la source
4
Vous demandez comment faire quelque chose que GraphQL, de par sa conception, ne prend pas en charge.
Travis Webb
12
Tapez simplement ces 40 champs et j'espère que vous ne faites pas de faute de frappe :)
Ska
32
Wow, je ne fais que commencer dans GraphQL, et c'est un WTF sérieux.
user949300
1
Il est logique qu'il ne soit pas pris en charge, imaginez que vous avez des objets Etudiant et Classe, que l'étudiant a un champ "classes" qui répertorie toutes les classes qu'il assiste, la classe a un champ "étudiants" qui répertorie tous les étudiants qui fréquentent cette classe. C'est une structure cyclique. Maintenant, si vous demandez pour tous les étudiants avec tous les champs, cela inclurait-il également tous les champs des cours renvoyés? Et ces classes ont des étudiants, leurs domaines seraient-ils également inclus? Et les étudiants ont des cours, ...
Buksy
J'avais cette question et c'était pour que je puisse voir ce qui était même disponible pour tirer. De nombreux clients GraphQL (par exemple GraphiQL, voir gatsbyjs.org/docs/running-queries-with-graphiql ) ont un explorateur de schémas qui utilise l'introspection pour vous présenter ce que vous pouvez tirer, si c'est la raison pour laquelle vous voulez tout obtenir ".
James

Réponses:

120

Malheureusement, ce que vous aimeriez faire n'est pas possible. GraphQL vous demande d'être explicite sur la spécification des champs que vous souhaitez renvoyer à partir de votre requête.

Peter Horne
la source
5
Ok, et si je demande un objet d'un formulaire inconnu du backend que je suis censé renvoyer ou renvoyer?
Meandre
19
@meandre, toute l'idée de graphql est qu'il n'existe pas de "forme inconnue".
s.meijer
2
@meandre, ma réponse ci-dessous pourrait vous être utile?
Tyrone Wilson
N'est-ce pas l'idée même de la plupart des langages et protocoles de requête d'API?, @Meandre
Clijsters
intéressant, c'est vraiment un état d'esprit différent lors de l'utilisation de graphql
andy mccullough
91

Oui, vous pouvez le faire en utilisant l' introspection . Faire une requête GraphQL comme (pour le type UserType )

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

et vous obtiendrez une réponse comme (les noms de champ réels dépendront de votre définition réelle de schéma / type)

{
  "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. ...)
      ]
    }
  }
}

Vous pouvez ensuite lire cette liste de champs dans votre client et créer dynamiquement une deuxième requête GraphQL pour obtenir tous ces champs.

Cela dépend du fait que vous connaissiez le nom du type pour lequel vous souhaitez obtenir les champs - si vous ne connaissez pas le type, vous pouvez rassembler tous les types et champs en utilisant une introspection comme

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

REMARQUE: ce sont les données GraphQL over-the-wire - vous êtes seul pour comprendre comment lire et écrire avec votre client réel. Votre bibliothèque javascript graphQL peut déjà utiliser l'introspection dans une certaine capacité, par exemple la commande apollo codegen utilise l'introspection pour générer des types.

Mark Chackerian
la source
On dirait que l'on devrait se soucier des types récursifs. Si vous descendez l'arborescence et êtes tombé sur un type qui se contient, sous une forme quelconque (liste, simple ou autre ..), vous pourriez être dans une récursion infinie.
Milos Grujic
Cela ne se produit pas réellement dans mon expérience avec cette requête particulière - la requête elle-même définit la profondeur de résolution.
Mark Chackerian
La réponse ci-dessus vous permet uniquement de rechercher les types de champs disponibles dans une requête. Il ne renvoie pas tous les champs d'objet "valeurs", ce qui correspond à la question d'origine.
quantdaddy
4
Selon la réponse, vous devez créer dynamiquement une deuxième requête basée sur les résultats de la première requête - j'ai laissé cela comme exercice pour le lecteur.
Mark Chackerian
39

Je suppose que le seul moyen de le faire est d'utiliser des fragments réutilisables:

fragment UserFragment on Users {
    id
    username
    count
} 

FetchUsers {
    users(id: "2") {
        ...UserFragment
    }
}
Tommy
la source
19
Si je faisais ça, alors je dois quand même écrire chaque nom de champ "au moins dans le fragment", sorcier ce que j'essayais d'éviter, il semble que GraphQL nous force à être explicites.
BlackSigma
comment ajouter ceci dans une requête POSTMan? ou jquery / UI framwork pour créer un JSON stringifié. Ce graphiQL semble inutile à des fins de développement réel.
mfaisalhyder
Ceci est uniquement à des fins de réutilisation.
Henok Tesfaye
@BlackSigma Compte tenu de la documentation GraphQL , cela devrait être la meilleure réponse acceptée
JP Ventura
4
@JPVentura: Non mon ami, il y a une différence entre la réutilisabilité et le caractère générique à la fois dans le concept et dans l'application. Le but du fragment est clair dans la documentation "GraphQL inclut des unités réutilisables appelées fragments." L'utilisation de fragment est utile, mais ce n'est pas la réponse à la question.
BlackSigma
11

J'ai rencontré le même problème lorsque j'ai eu besoin de charger des données de localisation que j'avais sérialisées dans la base de données à partir de l'API google places. En général, je voudrais que tout cela fonctionne avec des cartes, mais je ne voulais pas avoir à spécifier tous les champs à chaque fois.

Je travaillais dans Ruby donc je ne peux pas vous donner l'implémentation PHP mais le principe devrait être le même.

J'ai défini un type scalaire personnalisé appelé JSON qui renvoie simplement un objet JSON littéral.

L'implémentation de ruby ​​était comme ça (en utilisant graphql-ruby)

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

Puis je l'ai utilisé pour nos objets comme ça

field :location, Types::JsonType

J'utiliserais cela avec parcimonie, en l'utilisant uniquement là où vous savez que vous avez toujours besoin de tout l'objet JSON (comme je l'ai fait dans mon cas). Sinon, cela va à l'encontre de l'objet de GraphQL plus généralement.

Tyrone Wilson
la source
1
C'est exactement ce dont j'avais besoin, merci. Mon cas d'utilisation est que j'ai des chaînes traduisibles par l'utilisateur dans tout le système, et elles sont stockées en tant que json dans la base de données {"en": "Hello", "es": "Hola"}. Et comme chaque utilisateur peut implémenter son propre sous-ensemble de langues pour son cas d'utilisation, il n'est pas logique que l'interface utilisateur interroge tous les sous-ensembles possibles. Votre exemple fonctionne parfaitement.
Luke Ehresman
2

Le format de requête GraphQL a été conçu pour permettre:

  1. La forme de la requête et celle du résultat sont exactement les mêmes .
  2. Le serveur connaît exactement les champs demandés, ainsi le client ne télécharge que les données essentielles.

Cependant, selon la documentation GraphQL , vous pouvez créer des fragments afin de rendre les jeux de sélection plus réutilisables:

# Only most used selection properties

fragment UserDetails on User {
    id,
    username
} 

Ensuite, vous pouvez interroger tous les détails de l'utilisateur en:

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

Vous pouvez également ajouter des champs supplémentaires à côté de votre fragment :

FetchUserById($id: ID!) {
    users(id: $id) {
        ...UserDetails
        count
    }
}
JP Ventura
la source