View a markdown version of this page

GraphQL-Typen - AWS AppSync GraphQL

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

GraphQL-Typen

GraphQL unterstützt viele verschiedene Typen. Wie Sie im vorherigen Abschnitt gesehen haben, definieren Typen die Form oder das Verhalten Ihrer Daten. Sie sind die grundlegenden Bausteine eines GraphQL-Schemas.

Typen können in Eingaben und Ausgaben kategorisiert werden. Eingaben sind Typen, die als Argument für die speziellen Objekttypen (Query, usw.) übergeben werden dürfenMutation, wohingegen Ausgabetypen ausschließlich zum Speichern und Zurückgeben von Daten verwendet werden. Eine Liste von Typen und ihren Kategorisierungen ist unten aufgeführt:

  • Objekte: Ein Objekt enthält Felder, die eine Entität beschreiben. Ein Objekt könnte zum Beispiel so etwas wie ein Objekt book mit Feldern sein, die seine Eigenschaften beschreiben authorNamepublishingYear, wie, usw. Es handelt sich ausschließlich um Ausgabetypen.

  • Skalare: Dies sind primitive Typen wie int, string usw. Sie werden normalerweise Feldern zugewiesen. Am Beispiel des authorName Feldes könnte ihm der String Skalar zugewiesen werden, um einen Namen wie „John Smith“ zu speichern. Skalare können sowohl Eingabe- als auch Ausgabetypen sein.

  • Eingaben: Mit Eingaben können Sie eine Gruppe von Feldern als Argument übergeben. Sie sind sehr ähnlich wie Objekte strukturiert, können aber als Argumente an spezielle Objekte übergeben werden. Mithilfe von Eingaben können Sie Skalare, Aufzählungen und andere Eingaben in ihrem Gültigkeitsbereich definieren. Eingaben können nur Eingabetypen sein.

  • Spezialobjekte: Spezialobjekte führen Operationen durch, die den Status ändern, und übernehmen den Großteil der schweren Arbeit des Dienstes. Es gibt drei spezielle Objekttypen: Abfrage, Mutation und Abonnement. Abfragen rufen in der Regel Daten ab; Mutationen manipulieren Daten; Abonnements öffnen und halten eine bidirektionale Verbindung zwischen Clients und Servern für eine konstante Kommunikation aufrecht. Aufgrund ihrer Funktionalität werden spezielle Objekte weder eingegeben noch ausgegeben.

  • Enums: Enums sind vordefinierte Listen mit zulässigen Werten. Wenn Sie eine Enumeration aufrufen, können ihre Werte nur das sein, was in ihrem Gültigkeitsbereich definiert ist. Wenn Sie beispielsweise eine Aufzählung aufrufen, die eine Liste von trafficLights Ampeln darstellt, könnte sie Werte wie redLight und greenLight aber nicht haben. purpleLight Eine echte Ampel hat nur eine bestimmte Anzahl von Signalen, also könnten Sie sie mithilfe der Aufzählung definieren und bei der Referenzierung festlegen, dass sie die einzig zulässigen Werte sind. trafficLight Aufzählungen können sowohl Eingabe- als auch Ausgabetypen sein.

  • Unions/interfaces: Unions ermöglichen es Ihnen, ein oder mehrere Dinge in einer Anfrage zurückzugeben, je nachdem, welche Daten vom Client angefordert wurden. Wenn Sie beispielsweise einen Book Typ mit einem title Feld und einen Author Typ mit einem name Feld hätten, könnten Sie eine Vereinigung zwischen beiden Typen erstellen. Wenn Ihr Kunde eine Datenbank nach dem Ausdruck „Julius Caesar“ abfragen möchte, könnte die Gewerkschaft Julius Caesar (das Stück von William Shakespeare) von der Book title und Julius Caesar (der Autor von Commentarii de Bello Gallico) von der zurückgeben. Author name Unions können nur Ausgabetypen sein.

    Schnittstellen sind Gruppen von Feldern, die Objekte implementieren müssen. Dies ist den Schnittstellen in Programmiersprachen wie Java ein bisschen ähnlich, bei denen Sie die in der Schnittstelle definierten Felder implementieren müssen. Nehmen wir zum Beispiel an, Sie haben eine Schnittstelle mit dem Namen erstelltBook, die ein title Feld enthält. Nehmen wir an, Sie haben später einen Typ namens Novel that implemented erstelltBook. Sie Novel müssten ein title Feld einschließen. Sie Novel könnten jedoch auch andere Felder einbeziehen, die nicht in der Benutzeroberfläche enthalten sind, wie z. B. pageCount oderISBN. Schnittstellen können nur Ausgabetypen sein.

In den folgenden Abschnitten wird erklärt, wie jeder Typ in GraphQL funktioniert.

Objekte

GraphQL-Objekte sind der Haupttyp, den Sie im Produktionscode sehen werden. In GraphQL können Sie sich ein Objekt als eine Gruppierung verschiedener Felder vorstellen (ähnlich wie Variablen in anderen Sprachen), wobei jedes Feld durch einen Typ (normalerweise ein Skalar oder ein anderes Objekt) definiert wird, der einen Wert enthalten kann. Objekte stellen eine Dateneinheit dar, die retrieved/manipulated aus Ihrer Service-Implementierung stammen kann.

Objekttypen werden mithilfe des Type Schlüsselworts deklariert. Lassen Sie uns unser Schemabeispiel leicht modifizieren:

type Person { id: ID! name: String age: Int occupation: Occupation } type Occupation { title: String }

Die Objekttypen hier sind Person undOccupation. Jedes Objekt hat seine eigenen Felder mit eigenen Typen. Eine Funktion von GraphQL ist die Möglichkeit, Felder auf andere Typen festzulegen. Sie können sehen, dass das occupation Feld einen Occupation Objekttyp Person enthält. Wir können diese Verknüpfung herstellen, da GraphQL nur die Daten beschreibt und nicht die Implementierung des Dienstes.

Skalare

Skalare sind im Wesentlichen primitive Typen, die Werte enthalten. In gibt AWS AppSync es zwei Arten von Skalaren: die Standard-GraphQL-Skalare und Skalare. AWS AppSync Skalare werden normalerweise verwendet, um Feldwerte innerhalb von Objekttypen zu speichern. Zu den Standard-GraphQL-Typen gehören IntFloat, StringBoolean, und. ID Lassen Sie uns das vorherige Beispiel noch einmal verwenden:

type Person { id: ID! name: String age: Int occupation: Occupation } type Occupation { title: String }

Wenn wir die title Felder name und herausgreifen, enthalten beide einen String Skalar. Namekönnte einen Zeichenkettenwert wie "John Smith" zurückgeben und der Titel könnte etwas wie "" firefighter zurückgeben. Einige GraphQL-Implementierungen unterstützen auch benutzerdefinierte Skalare, die das Scalar Schlüsselwort verwenden und das Verhalten des Typs implementieren. AWS AppSync Derzeit werden jedoch keine benutzerdefinierten Skalare unterstützt. Eine Liste der Skalare finden Sie unter Skalartypen in. AWS AppSync

Eingaben

Aufgrund des Konzepts der Eingabe- und Ausgabetypen gibt es bestimmte Einschränkungen bei der Übergabe von Argumenten. Typen, die üblicherweise übergeben werden müssen, insbesondere Objekte, sind eingeschränkt. Sie können den Eingabetyp verwenden, um diese Regel zu umgehen. Eingaben sind Typen, die Skalare, Aufzählungen und andere Eingabetypen enthalten.

Eingaben werden mit dem Schlüsselwort definiert: input

type Person { id: ID! name: String age: Int occupation: Occupation } type Occupation { title: String } input personInput { id: ID! name: String age: Int occupation: occupationInput } input occupationInput { title: String }

Wie Sie sehen können, können wir separate Eingaben verwenden, die den ursprünglichen Typ nachahmen. Diese Eingaben werden bei Ihren Feldoperationen häufig wie folgt verwendet:

type Person { id: ID! name: String age: Int occupation: Occupation } type Occupation { title: String } input occupationInput { title: String } type Mutation { addPerson(id: ID!, name: String, age: Int, occupation: occupationInput): Person }

Beachten Sie, dass wir immer noch anstelle von, um eine Occupation zu erstellen, übergehen occupationInputPerson.

Dies ist nur ein Szenario für Eingaben. Sie müssen Objekte nicht unbedingt 1:1 kopieren, und im Produktionscode werden Sie es höchstwahrscheinlich nicht so verwenden. Es empfiehlt sich, GraphQL-Schemas zu nutzen, indem Sie nur das definieren, was Sie als Argumente eingeben müssen.

Dieselben Eingaben können auch in mehreren Operationen verwendet werden, wir empfehlen dies jedoch nicht. Idealerweise sollte jede Operation eine eigene Kopie der Eingaben enthalten, falls sich die Anforderungen des Schemas ändern.

Besondere Objekte

GraphQL reserviert einige Schlüsselwörter für spezielle Objekte, die einen Teil der Geschäftslogik für die Verarbeitung Ihrer retrieve/manipulate Schemadaten definieren. In einem Schema kann es höchstens eines dieser Schlüsselwörter geben. Sie dienen als Einstiegspunkte für alle angeforderten Daten, die Ihre Kunden für Ihren GraphQL-Dienst ausführen.

Spezielle Objekte werden ebenfalls mit dem type Schlüsselwort definiert. Obwohl sie anders als reguläre Objekttypen verwendet werden, ist ihre Implementierung sehr ähnlich.

Queries

Abfragen sind GET Operationen insofern sehr ähnlich, als sie einen schreibgeschützten Abruf durchführen, um Daten aus Ihrer Quelle abzurufen. In GraphQL Query definiert das alle Einstiegspunkte für Clients, die Anfragen an Ihren Server stellen. In Ihrer GraphQL-Implementierung wird es immer eine Query geben.

Hier sind die Query und geänderten Objekttypen, die wir in unserem vorherigen Schemabeispiel verwendet haben:

type Person { id: ID! name: String age: Int occupation: Occupation } type Occupation { title: String } type Query { people: [Person] }

Unser Query enthält ein Feld namenspeople, das eine Liste von Person Instanzen aus der Datenquelle zurückgibt. Nehmen wir an, wir müssen das Verhalten unserer Anwendung ändern, und jetzt müssen wir nur eine Liste der Occupation Instanzen für einen anderen Zweck zurückgeben. Wir könnten es einfach zur Abfrage hinzufügen:

type Query { people: [Person] occupations: [Occupation] }

In GraphQL können wir unsere Abfrage als einzige Quelle für Anfragen behandeln. Wie Sie sehen, ist dies potenziell viel einfacher als RESTful-Implementierungen, die möglicherweise unterschiedliche Endpunkte verwenden, um dasselbe (und) zu erreichen. .../api/1/people .../api/1/occupations

Angenommen, wir haben eine Resolver-Implementierung für diese Abfrage, können wir jetzt eine tatsächliche Abfrage durchführen. Solange der Query Typ existiert, müssen wir ihn explizit aufrufen, damit er im Code der Anwendung ausgeführt wird. Dies kann mit dem query Schlüsselwort erfolgen:

query getItems { people { name } occupations { title } }

Wie Sie sehen können, wird diese Abfrage aufgerufen getItems und gibt people (eine Liste von Person Objekten) und occupations (eine Liste von Occupation Objekten) zurück. peopleIn geben wir nur das name Feld von jedem zurückPerson, während wir das title Feld von jedem zurückgebenOccupation. Die Antwort könnte so aussehen:

{ "data": { "people": [ { "name": "John Smith" }, { "name": "Andrew Miller" }, . . . ], "occupations": [ { "title": "Firefighter" }, { "title": "Bookkeeper" }, . . . ] } }

Die Beispielantwort zeigt, wie die Daten der Form der Abfrage folgen. Jeder abgerufene Eintrag wird im Bereich des Felds aufgeführt. peopleund geben occupations Dinge als separate Listen zurück. Obwohl nützlich, könnte es praktischer sein, die Abfrage so zu ändern, dass sie eine Liste mit Namen und Berufen von Personen zurückgibt:

query getItems { people { name occupation { title } }

Dies ist eine legale Änderung, da unser Person Typ ein occupation Feld vom Typ Occupation enthält. Wenn wir im Gültigkeitsbereich von aufgeführt sindpeople, geben wir jedes Person Feld name zusammen mit dem zugehörigen Occupation von zurücktitle. Die Antwort könnte so aussehen:

} "data": { "people": [ { "name": "John Smith", "occupation": { "title": "Firefighter" } }, { "name": "Andrew Miller", "occupation": { "title": "Bookkeeper" } }, . . . ] } }
Mutations

Mutationen ähneln zustandsändernden Operationen wie PUT oderPOST. Sie führen einen Schreibvorgang durch, um Daten in der Quelle zu ändern, und rufen dann die Antwort ab. Sie definieren Ihre Einstiegspunkte für Datenänderungsanfragen. Im Gegensatz zu Abfragen kann eine Mutation je nach den Anforderungen des Projekts in das Schema aufgenommen werden oder nicht. Hier ist die Mutation aus dem Schemabeispiel:

type Mutation { addPerson(id: ID!, name: String, age: Int): Person }

Das addPerson Feld stellt einen Einstiegspunkt dar, der der Datenquelle einen Person hinzufügt. addPersonist der Feldname;id,name, und age sind die Parameter; und Person ist der Rückgabetyp. Rückblick auf den Person Typ:

type Person { id: ID! name: String age: Int occupation: Occupation }

Wir haben das occupation Feld hinzugefügt. Wir können dieses Feld jedoch nicht Occupation direkt auf setzen, da Objekte nicht als Argumente übergeben werden können; es handelt sich ausschließlich um Ausgabetypen. Wir sollten stattdessen eine Eingabe mit denselben Feldern als Argument übergeben:

input occupationInput { title: String }

Wir können unsere auch einfach aktualisierenaddPerson, um dies als Parameter einzubeziehen, wenn wir neue Person Instanzen erstellen:

type Mutation { addPerson(id: ID!, name: String, age: Int, occupation: occupationInput): Person }

Hier ist das aktualisierte Schema:

type Person { id: ID! name: String age: Int occupation: Occupation } type Occupation { title: String } input occupationInput { title: String } type Mutation { addPerson(id: ID!, name: String, age: Int, occupation: occupationInput): Person }

Beachten occupation Sie, dass das title Feld von bis occupationInput zur Fertigstellung der Erstellung des Occupation Objekts Person anstelle des Originalobjekts übergeben wird. Angenommen, wir haben eine Resolver-Implementierung füraddPerson, können wir jetzt eine tatsächliche Mutation durchführen. Solange der Mutation Typ existiert, müssen wir ihn explizit aufrufen, damit er im Code der Anwendung ausgeführt wird. Dies kann mit dem mutation Schlüsselwort erfolgen:

mutation createPerson { addPerson(id: ID!, name: String, age: Int, occupation: occupationInput) { name age occupation { title } } }

Diese Mutation heißt createPerson und addPerson ist die Operation. Um eine neue zu erstellenPerson, können wir die Argumente für id nameage, und eingebenoccupation. Im Bereich von addPerson können wir auch andere Felder wie nameage, usw. sehen. Dies ist Ihre Antwort; dies sind die Felder, die nach Abschluss des addPerson Vorgangs zurückgegeben werden. Hier ist der letzte Teil des Beispiels:

mutation createPerson { addPerson(id: "1", name: "Steve Powers", age: "50", occupation: "Miner") { id name age occupation { title } } }

Mit dieser Mutation könnte ein Ergebnis wie folgt aussehen:

{ "data": { "addPerson": { "id": "1", "name": "Steve Powers", "age": "50", "occupation": { "title": "Miner" } } } }

Wie Sie sehen können, hat die Antwort die von uns angeforderten Werte in demselben Format zurückgegeben, das in unserer Mutation definiert wurde. Es hat sich bewährt, alle Werte zurückzugeben, die geändert wurden, um Verwirrung zu vermeiden und die Notwendigkeit weiterer Abfragen in Zukunft zu vermeiden. Mutationen ermöglichen es Ihnen, mehrere Operationen in den Gültigkeitsbereich einzubeziehen. Sie werden sequentiell in der Reihenfolge ausgeführt, die in der Mutation angegeben ist. Wenn wir beispielsweise eine weitere Operation mit dem Namen erstellenaddOccupation, die der Datenquelle Berufsbezeichnungen hinzufügt, können wir diese in der nachfolgenden Mutation aufrufen. addPerson addPersonwird zuerst behandelt, gefolgt vonaddOccupation.

Subscriptions

Abonnements dienen WebSockets dazu, eine dauerhafte, bidirektionale Verbindung zwischen dem Server und seinen Clients herzustellen. In der Regel abonniert ein Client den Server oder hört ihm zu. Immer wenn der Server eine serverseitige Änderung vornimmt oder ein Ereignis ausführt, erhält der abonnierte Client die Updates. Diese Art von Protokoll ist nützlich, wenn mehrere Clients abonniert sind und über Änderungen auf dem Server oder auf anderen Clients informiert werden müssen. Beispielsweise können Abonnements verwendet werden, um Social-Media-Feeds zu aktualisieren. Es könnte zwei Benutzer geben, Benutzer A und Benutzer B, die beide automatische Benachrichtigungsaktualisierungen abonniert haben, wenn sie Direktnachrichten erhalten. Benutzer A auf Client A könnte eine Direktnachricht an Benutzer B auf Client B senden. Der Client von Benutzer A würde die Direktnachricht senden, die vom Server verarbeitet würde. Der Server würde dann die Direktnachricht an das Konto von Benutzer B senden und gleichzeitig eine automatische Benachrichtigung an Client B senden.

Hier ist ein Beispiel für aSubscription, das wir dem Schemabeispiel hinzufügen könnten:

type Subscription { personAdded: Person }

Das personAdded Feld sendet eine Nachricht an abonnierte Kunden, wenn der Datenquelle eine neue hinzugefügt Person wird. Angenommen, wir haben eine Resolver-Implementierung fürpersonAdded, können wir jetzt das Abonnement verwenden. Solange der Subscription Typ existiert, müssen wir ihn explizit aufrufen, damit er im Code der Anwendung ausgeführt wird. Dies kann mit dem subscription Schlüsselwort erfolgen:

subscription personAddedOperation { personAdded { id name } }

Das Abonnement wird aufgerufenpersonAddedOperation, und die Operation istpersonAdded. personAddedgibt die name Felder id und neuer Person Instanzen zurück. Wenn wir uns das Mutationsbeispiel ansehen, haben wir eine hinzugefügt, die diese Operation Person verwendet:

addPerson(id: "1", name: "Steve Powers", age: "50", occupation: "Miner")

Wenn unsere Kunden Updates für die neu hinzugefügten Updates abonniert hätten, könnte es seinPerson, dass sie nach den addPerson Läufen Folgendes sehen:

{ "data": { "personAdded": { "id": "1", "name": "Steve Powers" } } }

Im Folgenden finden Sie eine Zusammenfassung dessen, was Abonnements bieten:

Abonnements sind wechselseitige Kanäle, die es dem Client und dem Server ermöglichen, schnelle, aber stetige Updates zu erhalten. Sie verwenden in der Regel das WebSocket Protokoll, das standardisierte und sichere Verbindungen herstellt.

Abonnements sind flexibel, da sie den Aufwand für den Verbindungsaufbau reduzieren. Einmal abonniert, kann ein Kunde dieses Abonnement einfach für längere Zeiträume weiterverwenden. Sie nutzen die Computerressourcen in der Regel effizient, da Entwickler die Laufzeit des Abonnements individuell anpassen und konfigurieren können, welche Informationen angefordert werden.

Im Allgemeinen ermöglichen Abonnements dem Kunden, mehrere Abonnements gleichzeitig abzuschließen. In diesem Zusammenhang AWS AppSync werden Abonnements nur für den Empfang von Echtzeit-Updates vom AWS AppSync Dienst verwendet. Sie können nicht zur Durchführung von Abfragen oder Mutationen verwendet werden.

Die Hauptalternative zu Abonnements ist das Polling, bei dem in festgelegten Intervallen Abfragen gesendet werden, um Daten anzufordern. Dieser Prozess ist in der Regel weniger effizient als Abonnements und belastet sowohl den Client als auch das Backend stark.

Eine Sache, die in unserem Schemabeispiel nicht erwähnt wurde, war die Tatsache, dass Ihre speziellen Objekttypen auch in einem schema Stammverzeichnis definiert werden müssen. Wenn Sie also ein Schema exportieren AWS AppSync, könnte es so aussehen:

schema.graphql
schema { query: Query mutation: Mutation subscription: Subscription } . . . type Query { # code goes here } type Mutation { # code goes here } type Subscription { # code goes here }

Aufzählungen

Aufzählungen oder Enumerationen sind spezielle Skalare, die die zulässigen Argumente einschränken, die ein Typ oder Feld haben kann. Das bedeutet, dass jedes Mal, wenn eine Aufzählung im Schema definiert wird, der zugehörige Typ oder das zugehörige Feld auf die Werte in der Aufzählung beschränkt wird. Aufzählungen werden als Zeichenkettenskalare serialisiert. Beachten Sie, dass verschiedene Programmiersprachen GraphQL-Enumerationen unterschiedlich behandeln können. JavaScript Hat beispielsweise keine native Enum-Unterstützung, sodass die Enum-Werte stattdessen Int-Werten zugeordnet werden können.

Enumerationen werden mithilfe des Schlüsselworts definiert. enum Hier ein Beispiel:

enum trafficSignals { solidRed solidYellow solidGreen greenArrowLeft ... }

Beim Aufrufen der trafficLights Aufzählung können die Argumente nursolidRed, solidYellowsolidGreen, usw. sein. Es ist üblich, Aufzählungen zu verwenden, um Dinge darzustellen, die eine bestimmte, aber begrenzte Anzahl von Auswahlmöglichkeiten haben.

Unions/Interfaces

Siehe Schnittstellen und Unions in GraphQL.