Was ist der Sinn des Eingabetyps in GraphQL?

83

Könnten Sie bitte erklären, warum, wenn das Eingabeargument der Mutation ein Objekt ist, es ein Eingabetyp sein sollte ? Ich denke viel einfacher, nur Typ ohne Angabe von ID wiederzuverwenden .

Zum Beispiel:

type Sample {
  id: String
  name: String
}

input SampleInput {
  name: String
}

type RootMutation {
  addSample(sample: Sample): Sample  # <-- instead of it should be
  addSample(sample: SampleInput): Sample
}

Es ist in Ordnung für kleine Objekte, aber wenn Sie viele Objekte mit mehr als 10 Eigenschaften im Schema haben, wird dies zu einer Belastung.

Dmitry Dushkin
quelle
30
Eingabeobjekte müssen serialisierbar sein. Da Ausgabeobjekte Zyklen enthalten können, können sie nicht für die Eingabe wiederverwendet werden.
Jesse Buchanan
1
Jesse, es sieht nach genug Antwort aus! Sie können antworten und ich markiere es so.
Dmitry Dushkin
Ich frage mich,

Antworten:

99

Aus der Spezifikation:

Der GraphQL-Objekttyp (ObjectTypeDefinition) ... ist für die Wiederverwendung [als Eingabe] ungeeignet, da Objekttypen Felder enthalten können, die Argumente definieren oder Verweise auf Schnittstellen und Vereinigungen enthalten, von denen keines als Eingabeargument geeignet ist . Aus diesem Grund haben Eingabeobjekte im System einen separaten Typ.

Das ist der "offizielle Grund", aber es gibt mehrere praktische Gründe, warum Sie einen Objekttyp nicht als Eingabeobjekttyp oder einen Objekttyp als Eingabeobjekttyp verwenden können:

Funktionalität

Objekttypen und Eingabeobjekttypen haben beide Felder. Diese Felder haben jedoch unterschiedliche Eigenschaften, die widerspiegeln, wie diese Typen vom Schema verwendet werden. Ihr Schema definiert möglicherweise Argumente und eine Art Resolverfunktion für die Felder eines Objekttyps, aber diese Eigenschaften sind in einem Eingabekontext nicht sinnvoll (dh Sie können das Feld eines Eingabeobjekts nicht auflösen - es hat bereits einen expliziten Wert). . Ebenso können Standardwerte nur für Eingabeobjekttypfelder und nicht für Objekttypfelder angegeben werden.

Mit anderen Worten, dies scheint eine Vervielfältigung zu sein:

type Student {
  name: String
  grade: Grade
}

input StudentInput {
  name: String
  grade: Grade
}

Durch Hinzufügen von Funktionen, die entweder für Objekttypen oder für Eingabeobjekttypen spezifisch sind, wird jedoch deutlich, dass sie sich unterschiedlich verhalten:

type Student {
  name(preferred: Boolean): String
  grade: Grade
}

input StudentInput {
  name: String
  grade: Grade = F
}

Geben Sie Systemeinschränkungen ein

Typen in GraphQL werden in Ausgabetypen und Eingabetypen gruppiert .

Ausgabetypen sind Typen, die als Teil einer von einem GraphQL-Dienst erzeugten Antwort zurückgegeben werden können. Eingabetypen sind Typen, die gültige Eingaben für Feld- oder Direktivenargumente sind.

Es gibt Überschneidungen zwischen diesen beiden Gruppen (dh Skalare, Aufzählungen, Listen und Nicht-Nullen). Allerdings abstrakte Typen wie Gewerkschaften und Schnittstellen keinen Sinn in einem Eingabekontext machen und können nicht als Eingänge verwendet werden. Durch die Trennung von Objekttypen und Eingabeobjekttypen können Sie sicherstellen, dass ein abstrakter Typ niemals dort verwendet wird, wo ein Eingabetyp erwartet wird.

Schemadesign

Bei der Darstellung einer Entität in Ihrem Schema ist es wahrscheinlich, dass einige Entitäten tatsächlich Felder zwischen ihren jeweiligen Eingabe- und Ausgabetypen "gemeinsam nutzen":

type Student {
  firstName: String
  lastName: String
  grade: Grade
}

input StudentInput {
  firstName: String
  lastName: String
  grade: Grade
}

Objekttypen können jedoch (und in der Realität häufig) sehr komplexe Datenstrukturen modellieren:

type Student {
  fullName: String!
  classes: [Class!]!
  address: Address!
  emergencyContact: Contact
  # etc
}

Während diese Strukturen können in entsprechende Eingaben übersetzen (wir einen Studenten schaffen, so dass wir in einem Objekt darstellt , ihre Adresse auch passieren), oft sie es nicht tun - also vielleicht müssen wir den Schüler-Klassen von Klassen - ID und Abschnitt - ID angeben, nicht ein Objekt. In ähnlicher Weise haben wir möglicherweise Felder, die wir zurückgeben möchten, aber nicht mutieren möchten, oder umgekehrt (wie ein passwordFeld).

Darüber hinaus haben wir selbst für relativ einfache Entitäten häufig unterschiedliche Anforderungen an die Nullbarkeit zwischen Objekttypen und ihren "Gegenstück" -Eingabeobjekten. Oft möchten wir sicherstellen, dass ein Feld auch in einer Antwort zurückgegeben wird, aber wir möchten nicht dieselben Felder für unsere Eingabe benötigen. Zum Beispiel,

type Student {
  firstName: String!
  lastName: String!
}

input StudentInput {
  firstName: String
  lastName: String
}

Schließlich gibt es in vielen Schemata häufig keine Eins-zu-Eins-Zuordnung zwischen Objekttyp und Eingabeobjekttyp für eine bestimmte Entität. Ein gemeinsames Muster besteht darin, separate Eingabeobjekttypen für verschiedene Operationen zu verwenden, um die Eingabevalidierung auf Schemaebene weiter zu optimieren:

input CreateUserInput {
  firstName: String!
  lastName: String!
  email: String!
  password: String!
}

input UpdateUserInput {
  email: String
  password: String
}

Alle diese Beispiele veranschaulichen einen wichtigen Punkt: Während ein Eingabeobjekttyp manchmal einen Objekttyp widerspiegelt, ist es aufgrund von Geschäftsanforderungen weniger wahrscheinlich, dass dies in Produktionsschemata der Fall ist.

Daniel Rearden
quelle
4
Das ist in der Tat eine gute Antwort!
Charlie Ng
Korrigieren Sie mich, wenn ich falsch liege, aber in Ihrem Beispiel kann der Typ Gradein der Eingabe nicht wiederverwendet werden StudentInput, oder? Sie müssen entweder Inline-Felder im Eingabeobjekt oder ein GradeInputEingabeobjekt haben.
Matt
2
@ Matt Gute Frage! Im obigen Beispiel Gradehandelt es sich um einen Aufzählungstyp. Im Gegensatz zu Objekten, skalare Typen (wie String, Int, etc.) und Aufzählungen Typen können verwendet werden , wie beiden Eingangstypen und Ausgabetypen.
Daniel Rearden
nette Erklärung
Visakh Vijayan
Tolle Antwort!
Johnny
25

Jesses Kommentar ist richtig. Für eine formellere Antwort finden Sie hier den Auszug aus der GraphQL-Dokumentation zu Eingabetypen :

Der oben definierte Objekttyp ist für die Wiederverwendung hier ungeeignet, da Objekte Felder enthalten können, die Zirkelverweise oder Verweise auf Schnittstellen und Vereinigungen ausdrücken, von denen keines als Eingabeargument geeignet ist. Aus diesem Grund haben Eingabeobjekte im System einen separaten Typ.

AKTUALISIEREN

Seit ich es gepostet habe, habe ich festgestellt, dass Zirkelverweise tatsächlich akzeptabel sind, solange diese nicht zulässig sind (oder es würde eine unendliche Kette deklarieren). Es gibt jedoch noch andere Einschränkungen (z. B. Schnittstellen), die ein separates Typsystem für Eingaben erforderlich zu machen scheinen.

LB2
quelle
4
Es gibt hier ein bisschen mehr Diskussion darüber, warum diese Einschränkung besteht: github.com/graphql/graphql-js/issues/599
Cam Jackson