Come interrogare tutti i campi di tipo GraphQL senza scrivere una lunga query?


130

Supponi di avere un tipo GraphQL e include molti campi. Come interrogare tutti i campi senza scrivere una lunga query che includa i nomi di tutti i campi?

Ad esempio, se ho questi campi:

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

        ];
    }

Per interrogare tutti i campi di solito la query è qualcosa del genere:

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

Ma voglio un modo per avere gli stessi risultati senza scrivere tutti i campi, qualcosa del genere:

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

C'è un modo per farlo in GraphQL?

Sto usando la libreria Folkloreatelier / laravel-graphql .


4
Stai chiedendo come fare qualcosa che GraphQL, in base alla progettazione, non supporta.
Travis Webb

12
Basta digitare quei 40 campi e sperare di non fare un errore di battitura :)
Ska

32
Wow, sto appena iniziando in GraphQL e questo è un serio WTF.
user949300

1
Ha senso che non sia supportato, immagina di avere oggetti Studente e Classe, studente ha campo "classi" che elenca tutte le classi che frequenta, classe ha campo "studenti" che elenca tutti gli studenti che frequentano quella classe. Questa è una struttura ciclica. Ora, se richiedi per tutti gli studenti con tutti i campi, verranno inclusi anche tutti i campi delle classi restituite? E quelle classi hanno studenti, dovrebbero essere inclusi anche i loro campi? E gli studenti hanno lezioni, ...
Buksy

Avevo questa domanda ed è stato così che ho potuto vedere cosa era disponibile per tirare. Molti client GraphQL (ad esempio GraphiQL, vedere gatsbyjs.org/docs/running-queries-with-graphiql ) hanno un esploratore di schemi che utilizza l'introspezione per presentarti ciò che puoi estrarre, se questo è il motivo per cui vuoi ottenere "tutto ".
James il

Risposte:


120

Purtroppo quello che vorresti fare non è possibile. GraphQL richiede che tu sia esplicito nello specificare quali campi desideri restituire dalla tua query.


5
Ok, e se richiedo un oggetto di un modulo sconosciuto dal backend che dovrei inviare tramite proxy o rimandare?
meandre

19
@meandre, l'intera idea di graphql è che non esiste una "forma sconosciuta".
s.meijer

2
@meandre, la mia risposta qui sotto potrebbe esserti utile?
Tyrone Wilson

Non è l'idea della maggior parte dei linguaggi e dei protocolli di query API ?, @meandre
Clijsters

interessante, è davvero una mentalità diversa quando si utilizza graphql
andy mccullough il

91

Sì, puoi farlo usando l' introspezione . Crea una query GraphQL come (per tipo UserType )

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

e riceverai una risposta del tipo (i nomi dei campi effettivi dipenderanno dalla definizione dello schema / tipo effettivo)

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

È quindi possibile leggere questo elenco di campi nel client e creare dinamicamente una seconda query GraphQL per ottenere tutti questi campi.

Questo si basa sul fatto che tu conosca il nome del tipo per il quale desideri ottenere i campi: se non conosci il tipo, puoi riunire tutti i tipi e campi usando l'introspezione come

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

NOTA: questi sono i dati GraphQL over-the-wire: sei da solo per capire come leggere e scrivere con il tuo client effettivo. La tua libreria javascript graphQL potrebbe già utilizzare l'introspezione in qualche modo, ad esempio il comando apollo codegen utilizza l'introspezione per generare i tipi.


Sembra che si dovrebbe esprimere attenzione ai tipi ricorsivi. Se scendi dall'albero e ti imbatti in un tipo che contiene se stesso, in qualche forma (elenco, singolo o altro ..), potresti trovarti in una ricorsione infinita.
Milos Grujic

Ciò in realtà non accade nella mia esperienza con questa particolare query: la query stessa definisce la profondità di risoluzione.
Mark Chackerian

La risposta precedente consente solo di interrogare i tipi di campi disponibili in una query. Non restituisce tutti i "valori" dei campi oggetto, che è l'argomento della domanda originale.
quantdaddy

4
Secondo la risposta, devi costruire dinamicamente una seconda query in base ai risultati della prima query - l'ho lasciata come esercizio per il lettore.
Mark Chackerian

39

Immagino che l'unico modo per farlo sia utilizzando frammenti riutilizzabili:

fragment UserFragment on Users {
    id
    username
    count
} 

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

19
Se l'ho fatto, allora devo ancora scrivere ogni nome di campo "almeno nel frammento", e quello che stavo cercando di evitare, sembra che GraphQL ci costringa ad essere espliciti.
BlackSigma

come aggiungerlo in una query POSTMan? o jquery / UI framwork per creare un JSON a stringhe. Questo graphiQL sembra inutile per il vero scopo di sviluppo.
mfaisalhyder

Questo è solo a scopo di riutilizzo.
Henok Tesfaye

@BlackSigma Considerando la documentazione GraphQL , questa dovrebbe essere la migliore risposta accettata
JP Ventura

4
@JPVentura: No amico mio, c'è una differenza tra riutilizzabilità e carattere jolly sia nel concetto che nell'applicazione. Lo scopo del frammento è chiaro nella documentazione "GraphQL include unità riutilizzabili chiamate frammenti". L'uso del frammento è utile, ma non è la risposta alla domanda.
BlackSigma

11

Ho affrontato lo stesso problema quando avevo bisogno di caricare i dati sulla posizione che avevo serializzato nel database dall'API di Google Places. Generalmente vorrei tutto in modo che funzioni con le mappe, ma non volevo specificare tutti i campi ogni volta.

Lavoravo in Ruby quindi non posso darti l'implementazione di PHP, ma il principio dovrebbe essere lo stesso.

Ho definito un tipo scalare personalizzato chiamato JSON che restituisce solo un oggetto JSON letterale.

L'implementazione di ruby ​​era così (usando graphql-ruby)

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

Poi l'ho usato per i nostri oggetti in questo modo

field :location, Types::JsonType

Lo userei con molta parsimonia, usandolo solo dove sai che hai sempre bisogno dell'intero oggetto JSON (come ho fatto nel mio caso). Altrimenti sta sconfiggendo l'oggetto di GraphQL più in generale.


1
Questo è esattamente ciò di cui avevo bisogno, grazie. Il mio caso d'uso è che ho stringhe traducibili dall'utente in tutto il sistema e sono memorizzate come json nel db come {"en": "Hello", "es": "Hola"}. E poiché ogni utente può implementare il proprio sottoinsieme di lingue per il proprio caso d'uso, non ha senso che l'interfaccia utente interroghi ogni possibile sottoinsieme. Il tuo esempio funziona perfettamente.
Luke Ehresman

2

Il formato di query GraphQL è stato progettato per consentire:

  1. Sia la forma della query che quella del risultato sono esattamente le stesse .
  2. Il server conosce esattamente i campi richiesti, quindi il client scarica solo i dati essenziali.

Tuttavia, secondo la documentazione di GraphQL , è possibile creare frammenti per rendere i set di selezione più riutilizzabili:

# Only most used selection properties

fragment UserDetails on User {
    id,
    username
} 

Quindi puoi interrogare tutti i dettagli dell'utente:

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

Puoi anche aggiungere campi aggiuntivi accanto al frammento :

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.