Wie kann man alle Felder vom Typ GraphQL abfragen, ohne eine lange Abfrage zu schreiben?

130

Angenommen, Sie haben einen GraphQL-Typ, der viele Felder enthält. Wie kann man alle Felder abfragen, ohne eine lange Abfrage aufzuschreiben, die die Namen aller Felder enthält?

Zum Beispiel, wenn ich diese Felder habe:

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

        ];
    }

Um alle Felder abzufragen, sieht die Abfrage normalerweise folgendermaßen aus:

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

Aber ich möchte einen Weg finden, um die gleichen Ergebnisse zu erzielen, ohne alle Felder zu schreiben.

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

Gibt es eine Möglichkeit, dies in GraphQL zu tun?

Ich verwende die Folkloreatelier / Laravel-Graphql- Bibliothek.

BlackSigma
quelle
4
Sie fragen, wie Sie etwas tun sollen, das GraphQL von Natur aus nicht unterstützt.
Travis Webb
12
Geben Sie einfach diese 40 Felder ein und hoffen Sie, dass Sie keinen Tippfehler machen :)
Ska
32
Wow, ich fange gerade mit GraphQL an und das ist eine ernsthafte WTF.
user949300
1
Es ist sinnvoll, dass es nicht unterstützt wird. Stellen Sie sich vor, Sie haben Schüler- und Klassenobjekte. Der Schüler hat ein Feld "Klassen", in dem alle Klassen aufgelistet sind, an denen er teilnimmt. Die Klasse hat ein Feld "Schüler", in dem alle Schüler aufgelistet sind, die an dieser Klasse teilnehmen. Das ist eine zyklische Struktur. Wenn Sie nun alle Schüler mit allen Feldern anfordern, würde dies auch alle zurückgegebenen Klassenfelder einschließen? Und diese Klassen haben Schüler, würden ihre Felder auch einbezogen werden? Und die Schüler haben Unterricht, ...
Buksy

Antworten:

119

Leider ist das, was Sie tun möchten, nicht möglich. In GraphQL müssen Sie explizit angeben, welche Felder von Ihrer Abfrage zurückgegeben werden sollen.

Peter Horne
quelle
5
Ok, und wenn ich vom Backend ein Objekt unbekannter Form anfordere, das ich vertreten oder zurücksenden soll?
Meandre
19
@meandre, die ganze Idee von graphql ist, dass es keine "unbekannte Form" gibt.
am
2
@meandre, könnte meine Antwort unten für Sie von Nutzen sein?
Tyrone Wilson
Ist es nicht die ganze Idee der meisten API-Abfragesprachen und -protokolle?, @Meandre
Clijsters
Interessant, es ist wirklich eine andere Denkweise bei der Verwendung von graphql
andy mccullough
91

Ja, Sie können dies mit Selbstbeobachtung tun . Machen Sie eine GraphQL-Abfrage wie (für Typ UserType )

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

und Sie erhalten eine Antwort wie (die tatsächlichen Feldnamen hängen von Ihrer tatsächlichen Schema- / Typdefinition ab).

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

Sie können dann diese Liste von Feldern in Ihrem Client lesen und dynamisch eine zweite GraphQL-Abfrage erstellen, um alle diese Felder abzurufen.

Dies setzt voraus, dass Sie den Namen des Typs kennen, für den Sie die Felder abrufen möchten. Wenn Sie den Typ nicht kennen, können Sie alle Typen und Felder mithilfe von Introspektion wie zusammenstellen

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

HINWEIS: Dies sind die drahtgebundenen GraphQL-Daten. Sie müssen selbst herausfinden, wie Sie mit Ihrem tatsächlichen Client lesen und schreiben können. Ihre graphQL-Javascript-Bibliothek verwendet möglicherweise bereits in gewisser Weise Introspektion. Beispielsweise verwendet der Befehl apollo codegen Introspektion, um Typen zu generieren.

Mark Chackerian
quelle
Scheint, als sollte man sich um rekursive Typen kümmern. Wenn Sie den Baum hinuntergegangen sind und auf einen Typ gestoßen sind, der sich in irgendeiner Form enthält (Liste, Single oder andere ..), könnten Sie sich auf eine unendliche Rekursion einlassen.
Milos Grujic
Das passiert meiner Erfahrung nach mit dieser speziellen Abfrage nicht wirklich - die Abfrage selbst definiert die Auflösungstiefe.
Mark Chackerian
Mit der obigen Antwort können Sie nur die in einer Abfrage verfügbaren Feldtypen abfragen. Es werden nicht alle Objektfelder "Werte" zurückgegeben, worum es in der ursprünglichen Frage geht.
Quantdaddy
4
Gemäß der Antwort müssen Sie dynamisch eine zweite Abfrage basierend auf den Ergebnissen der ersten Abfrage erstellen - das habe ich als Übung für den Leser hinterlassen.
Mark Chackerian
39

Ich denke, der einzige Weg, dies zu tun, ist die Verwendung wiederverwendbarer Fragmente:

fragment UserFragment on Users {
    id
    username
    count
} 

FetchUsers {
    users(id: "2") {
        ...UserFragment
    }
}
Tommy
quelle
19
Wenn ich das getan habe, muss ich trotzdem jeden Feldnamen "zumindest in das Fragment" schreiben, was ich zu vermeiden versuchte, scheint GraphQL uns zu zwingen, explizit zu sein.
BlackSigma
Wie füge ich dies in eine POSTMan-Abfrage ein? oder jquery / UI-Framwork, um einen stringifizierten JSON zu erstellen. Diese Grafik scheint für den eigentlichen Entwicklungszweck nutzlos zu sein.
Mfaisalhyder
Dies dient ausschließlich der Wiederverwendung.
Henok Tesfaye
@ BlackSigma In Anbetracht der GraphQL-Dokumentation sollte dies als beste Antwort akzeptiert werden
JP Ventura
4
@JPVentura: Nein, mein Freund, es gibt einen Unterschied zwischen Wiederverwendbarkeit und Platzhalter sowohl im Konzept als auch in der Anwendung. Der Zweck des Fragments wird in der Dokumentation "GraphQL enthält wiederverwendbare Einheiten, die als Fragmente bezeichnet werden" deutlich. Die Verwendung von Fragmenten ist nützlich, aber nicht die Antwort auf die Frage.
BlackSigma
11

Ich hatte das gleiche Problem, als ich Standortdaten laden musste, die ich über die Google Places-API in die Datenbank serialisiert hatte. Im Allgemeinen möchte ich das Ganze, damit es mit Karten funktioniert, aber ich wollte nicht jedes Mal alle Felder angeben müssen.

Ich habe in Ruby gearbeitet, daher kann ich Ihnen die PHP-Implementierung nicht geben, aber das Prinzip sollte dasselbe sein.

Ich habe einen benutzerdefinierten Skalartyp namens JSON definiert, der nur ein wörtliches JSON-Objekt zurückgibt.

Die Ruby-Implementierung war so (mit graphql-ruby)

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

Dann habe ich es für unsere Objekte so benutzt

field :location, Types::JsonType

Ich würde dies jedoch sehr sparsam verwenden und es nur dort verwenden, wo Sie wissen, dass Sie immer das gesamte JSON-Objekt benötigen (wie ich es in meinem Fall getan habe). Andernfalls wird das Objekt von GraphQL allgemeiner besiegt.

Tyrone Wilson
quelle
1
Genau das habe ich gebraucht, danke. Mein Anwendungsfall ist, dass ich im gesamten System benutzerübersetzbare Zeichenfolgen habe, die als json in der Datenbank wie gespeichert sind {"en": "Hello", "es": "Hola"}. Und da jeder Benutzer seine eigene Teilmenge von Sprachen für seinen Anwendungsfall implementieren kann, ist es für die Benutzeroberfläche nicht sinnvoll, jede mögliche Teilmenge abzufragen. Ihr Beispiel funktioniert perfekt.
Luke Ehresman
2

Das GraphQL-Abfrageformat wurde entwickelt, um Folgendes zu ermöglichen:

  1. Sowohl die Abfrage- als auch die Ergebnisform sind exakt gleich .
  2. Der Server kennt die angeforderten Felder genau , daher lädt der Client nur wesentliche Daten herunter .

Gemäß der GraphQL-Dokumentation können Sie jedoch Fragmente erstellen , um Auswahlsätze wiederverwendbarer zu machen:

# Only most used selection properties

fragment UserDetails on User {
    id,
    username
} 

Dann können Sie alle Benutzerdetails abfragen, indem Sie:

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

Sie können neben Ihrem Fragment auch zusätzliche Felder hinzufügen :

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