View a markdown version of this page

Types de GraphQL - AWS AppSync GraphQL

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

Types de GraphQL

GraphQL prend en charge de nombreux types différents. Comme vous l'avez vu dans la section précédente, les types définissent la forme ou le comportement de vos données. Ce sont les éléments fondamentaux d'un schéma GraphQL.

Les types peuvent être classés en entrées et en sorties. Les entrées sont des types qui peuvent être transmis en tant qu'argument pour les types d'objets spéciaux (Query, etc.)Mutation, tandis que les types de sortie sont strictement utilisés pour stocker et renvoyer des données. Vous trouverez ci-dessous une liste des types et de leurs catégories :

  • Objets  : un objet contient des champs décrivant une entité. Par exemple, un objet peut être quelque chose comme un book avec des champs décrivant ses caractéristiques comme authorNamepublishingYear, etc. Ce sont strictement des types de sortie.

  • Scalaires  : ce sont des types primitifs tels que int, string, etc. Ils sont généralement affectés à des champs. En utilisant le authorName champ comme exemple, on pourrait lui attribuer le String scalaire pour stocker un nom tel que « John Smith ». Les scalaires peuvent être des types d'entrée et de sortie.

  • Entrées  : Les entrées vous permettent de transmettre un groupe de champs en tant qu'argument. Ils sont structurés de manière très similaire à celle des objets, mais ils peuvent être transmis en tant qu'arguments à des objets spéciaux. Les entrées vous permettent de définir des scalaires, des énumérations et d'autres entrées dans son champ d'application. Les entrées ne peuvent être que des types d'entrée.

  • Objets spéciaux  : Les objets spéciaux effectuent des opérations de changement d'état et effectuent l'essentiel du travail du service. Il existe trois types d'objets spéciaux : requête, mutation et abonnement. Les requêtes récupèrent généralement des données ; les mutations manipulent les données ; les abonnements s'ouvrent et maintiennent une connexion bidirectionnelle entre les clients et les serveurs pour une communication constante. Les objets spéciaux ne sont ni des entrées ni des sorties compte tenu de leurs fonctionnalités.

  • Énumérations  : Les énumérations sont des listes prédéfinies de valeurs légales. Si vous appelez une énumération, ses valeurs ne peuvent être que celles définies dans sa portée. Par exemple, si vous aviez une énumération appelée trafficLights représentant une liste de feux de circulation, elle pourrait avoir des valeurs telles que redLight et greenLight mais paspurpleLight. Un vrai feu de signalisation n'aura qu'un nombre limité de signaux, vous pouvez donc utiliser l'énumération pour les définir et les forcer à être les seules valeurs légales lors du référencementtrafficLight. Les énumérations peuvent être à la fois du type d'entrée et du type de sortie.

  • Unions/interfaces: Les syndicats vous permettent de renvoyer un ou plusieurs éléments dans une demande en fonction des données demandées par le client. Par exemple, si vous aviez un Book type avec un title champ et un Author type avec un name champ, vous pourriez créer une union entre les deux types. Si votre client souhaitait interroger une base de données pour l'expression « Jules César », le syndicat pourrait renvoyer Jules César (la pièce de William Shakespeare) du Book title et Jules César (l'auteur de Commentarii de Bello Gallico) du. Author name Les unions ne peuvent être que des types de sortie.

    Les interfaces sont des ensembles de champs que les objets doivent implémenter. C'est un peu similaire aux interfaces dans des langages de programmation comme Java où vous devez implémenter les champs définis dans l'interface. Par exemple, supposons que vous ayez créé une interface appelée Book qui contenait un title champ. Supposons que vous ayez créé plus tard un type appelé Novel that implementedBook. Vous Novel devrez inclure un title champ. Cependant, vous Novel pouvez également inclure d'autres champs qui ne figurent pas dans l'interface, tels que pageCount ouISBN. Les interfaces ne peuvent être que des types de sortie.

Les sections suivantes expliquent comment chaque type fonctionne dans GraphQL.

Objets

Les objets GraphQL sont le type principal que vous verrez dans le code de production. Dans GraphQL, vous pouvez considérer un objet comme un regroupement de différents champs (similaires aux variables dans d'autres langages), chaque champ étant défini par un type (généralement un scalaire ou un autre objet) qui peut contenir une valeur. Les objets représentent une unité de données qui peut retrieved/manipulated provenir de l'implémentation de votre service.

Les types d'objets sont déclarés à l'aide du Type mot clé. Modifions légèrement notre exemple de schéma :

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

Les types d'objets ici sont Person etOccupation. Chaque objet possède ses propres champs avec ses propres types. L'une des fonctionnalités de GraphQL est la possibilité de définir d'autres types de champs. Vous pouvez voir que le occupation champ Person contient un type d'Occupationobjet. Nous pouvons faire cette association car GraphQL ne décrit que les données et non l'implémentation du service.

Scalaires

Les scalaires sont essentiellement des types primitifs qui contiennent des valeurs. Dans AWS AppSync, il existe deux types de scalaires : les scalaires GraphQL par défaut et les scalaires. AWS AppSync Les scalaires sont généralement utilisés pour stocker des valeurs de champ dans des types d'objets. Les types GraphQL par défaut incluent IntFloat, StringBoolean, etID. Reprenons l'exemple précédent :

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

Si l'on distingue les title champs name et, les deux contiennent un String scalaire. Namepourrait renvoyer une valeur de chaîne comme « John Smith » et le titre pourrait renvoyer quelque chose comme « firefighter ». Certaines implémentations GraphQL prennent également en charge les scalaires personnalisés en utilisant le Scalar mot-clé et en implémentant le comportement du type. Cependant, il ne prend AWS AppSync actuellement pas en charge les scalaires personnalisés. Pour une liste des scalaires, voir Types scalaires dans. AWS AppSync

Inputs

En raison du concept de types d'entrée et de sortie, certaines restrictions sont en place lors de la transmission d'arguments. Les types qui doivent généralement être transmis, en particulier les objets, sont restreints. Vous pouvez utiliser le type d'entrée pour contourner cette règle. Les entrées sont des types qui contiennent des scalaires, des énumérations et d'autres types d'entrées.

Les entrées sont définies à l'aide du input mot clé :

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 }

Comme vous pouvez le constater, nous pouvons avoir des entrées séparées qui imitent le type d'origine. Ces entrées seront souvent utilisées dans vos opérations sur le terrain, comme suit :

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 }

Notez que nous sommes toujours en train de passer occupationInput à la place de Occupation pour créer unPerson.

Il ne s'agit que d'un scénario pour les entrées. Ils n'ont pas nécessairement besoin de copier les objets au 1:1. et dans le code de production, vous ne l'utiliserez probablement pas comme ça. Il est recommandé de tirer parti des schémas GraphQL en définissant uniquement ce que vous devez saisir en tant qu'arguments.

De plus, les mêmes entrées peuvent être utilisées dans plusieurs opérations, mais nous vous déconseillons de le faire. Chaque opération doit idéalement contenir sa propre copie unique des entrées au cas où les exigences du schéma changeraient.

Objets spéciaux

GraphQL réserve quelques mots clés pour des objets spéciaux qui définissent une partie de la logique métier régissant la façon dont votre schéma traite les retrieve/manipulate données. Un schéma ne peut contenir qu'un seul de ces mots clés. Ils servent de points d'entrée pour toutes les données demandées que vos clients exécutent sur votre service GraphQL.

Les objets spéciaux sont également définis à l'aide du type mot clé. Bien qu'ils soient utilisés différemment des types d'objets classiques, leur implémentation est très similaire.

Queries

Les requêtes sont très similaires aux GET opérations en ce sens qu'elles effectuent une extraction en lecture seule pour obtenir des données depuis votre source. Dans GraphQL, il Query définit tous les points d'entrée pour les clients qui adressent des requêtes à votre serveur. Il y en aura toujours Query dans votre implémentation GraphQL.

Voici les types Query d'objets modifiés que nous avons utilisés dans notre exemple de schéma précédent :

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

Notre Query contient un champ appelé people qui renvoie une liste d'Personinstances provenant de la source de données. Supposons que nous devions modifier le comportement de notre application et que nous devions maintenant renvoyer une liste des Occupation instances uniquement dans un but distinct. Nous pourrions simplement l'ajouter à la requête :

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

Dans GraphQL, nous pouvons traiter notre requête comme la source unique de requêtes. Comme vous pouvez le constater, cela est potentiellement beaucoup plus simple que les implémentations RESTful qui peuvent utiliser différents points de terminaison pour obtenir la même chose (.../api/1/peopleet). .../api/1/occupations

En supposant que nous ayons une implémentation de résolveur pour cette requête, nous pouvons maintenant effectuer une requête réelle. Tant que le Query type existe, nous devons l'appeler explicitement pour qu'il s'exécute dans le code de l'application. Cela peut être fait à l'aide du query mot clé :

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

Comme vous pouvez le voir, cette requête est appelée getItems et renvoie people (une liste d'Personobjets) et occupations (une liste d'Occupationobjets). Danspeople, nous ne renvoyons que le name champ de chacunPerson, tandis que nous renvoyons le title champ de chacunOccupation. La réponse peut ressembler à ceci :

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

L'exemple de réponse montre comment les données suivent la forme de la requête. Chaque entrée récupérée est répertoriée dans la portée du champ. peopleet occupations renvoient les articles sous forme de listes séparées. Bien que cela soit utile, il peut être plus pratique de modifier la requête pour renvoyer une liste des noms et des professions des personnes :

query getItems { people { name occupation { title } }

Il s'agit d'une modification légale car notre Person type contient un occupation champ de typeOccupation. Lorsque la liste est répertoriée dans le cadre depeople, nous Person renvoyons chacun name avec son identifiant associé Occupation partitle. La réponse peut ressembler à ceci :

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

Les mutations sont similaires aux opérations de changement d'état comme PUT ouPOST. Ils effectuent une opération d'écriture pour modifier les données de la source, puis récupèrent la réponse. Ils définissent vos points d'entrée pour les demandes de modification des données. Contrairement aux requêtes, une mutation peut être incluse ou non dans le schéma en fonction des besoins du projet. Voici la mutation à partir de l'exemple de schéma :

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

Le addPerson champ représente un point d'entrée qui ajoute un Person à la source de données. addPersonest le nom du champ ;id,name, et age sont les paramètres ; et Person est le type de retour. En repensant au Person type :

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

Nous avons ajouté le occupation champ. Cependant, nous ne pouvons pas définir ce champ sur Occupation directement car les objets ne peuvent pas être transmis en tant qu'arguments ; ce sont uniquement des types de sortie. Nous devrions plutôt passer une entrée avec les mêmes champs en argument :

input occupationInput { title: String }

Nous pouvons également facilement le mettre à jour addPerson pour l'inclure en tant que paramètre lors de la création de nouvelles Person instances :

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

Voici le schéma mis à jour :

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 }

Notez que cela occupation passera dans le title champ de occupationInput pour terminer la création de l'objet à la Person place de l'Occupationobjet d'origine. En supposant que nous ayons une implémentation de résolveur pouraddPerson, nous pouvons maintenant effectuer une véritable mutation. Tant que le Mutation type existe, nous devons l'appeler explicitement pour qu'il s'exécute dans le code de l'application. Cela peut être fait à l'aide du mutation mot clé :

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

Cette mutation s'appellecreatePerson, et addPerson c'est l'opération. Pour en créer un nouveauPerson, nous pouvons entrer les arguments pour idname,age, etoccupation. Dans le cadre deaddPerson, nous pouvons également voir d'autres champs tels que nameage, etc. Il s'agit de votre réponse ; ce sont les champs qui seront renvoyés une fois l'addPersonopération terminée. Voici la dernière partie de l'exemple :

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

En utilisant cette mutation, le résultat pourrait ressembler à ceci :

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

Comme vous pouvez le constater, la réponse a renvoyé les valeurs que nous avions demandées dans le même format que celui défini dans notre mutation. Il est recommandé de renvoyer toutes les valeurs qui ont été modifiées afin de réduire la confusion et la nécessité de nouvelles requêtes à l'avenir. Les mutations vous permettent d'inclure plusieurs opérations dans son champ d'application. Ils seront exécutés séquentiellement dans l'ordre indiqué dans la mutation. Par exemple, si nous créons une autre opération appelée addOccupation qui ajoute des titres de poste à la source de données, nous pouvons l'appeler dans la mutation suivanteaddPerson. addPersonsera traité en premier, suivi deaddOccupation.

Subscriptions

Les abonnements WebSockets permettent d'établir une connexion bidirectionnelle durable entre le serveur et ses clients. En règle générale, un client s'abonne au serveur ou l'écoute. Chaque fois que le serveur effectue une modification côté serveur ou exécute un événement, le client abonné reçoit les mises à jour. Ce type de protocole est utile lorsque plusieurs clients sont abonnés et doivent être informés des modifications apportées au serveur ou à d'autres clients. Par exemple, les abonnements peuvent être utilisés pour mettre à jour les flux de réseaux sociaux. Il peut y avoir deux utilisateurs, l'utilisateur A et l'utilisateur B, qui sont tous deux abonnés à des mises à jour automatiques chaque fois qu'ils reçoivent des messages directs. L'utilisateur A du client A pourrait envoyer un message direct à l'utilisateur B du client B. Le client de l'utilisateur A enverrait le message direct, qui serait traité par le serveur. Le serveur enverrait ensuite le message direct au compte de l'utilisateur B tout en envoyant une notification automatique au client B.

Voici un exemple Subscription que nous pourrions ajouter à l'exemple de schéma :

type Subscription { personAdded: Person }

Le personAdded champ envoie un message aux clients abonnés chaque fois qu'une nouvelle information Person est ajoutée à la source de données. En supposant que nous ayons une implémentation de résolveur pourpersonAdded, nous pouvons maintenant utiliser l'abonnement. Tant que le Subscription type existe, nous devons l'appeler explicitement pour qu'il s'exécute dans le code de l'application. Cela peut être fait à l'aide du subscription mot clé :

subscription personAddedOperation { personAdded { id name } }

L'abonnement est appelé personAddedOperation et l'opération l'estpersonAdded. personAddedrenverra les name champs id et des nouvelles Person instances. En regardant l'exemple de mutation, nous avons ajouté un Person en utilisant cette opération :

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

Si nos clients étaient abonnés aux mises à jour des articles récemment ajoutésPerson, ils pourraient voir ceci après les addPerson exécutions :

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

Vous trouverez ci-dessous un résumé de ce que proposent les abonnements :

Les abonnements sont des canaux bidirectionnels qui permettent au client et au serveur de recevoir des mises à jour rapides mais régulières. Ils utilisent généralement le WebSocket protocole, qui crée des connexions standardisées et sécurisées.

Les abonnements sont souples dans la mesure où ils réduisent les frais de configuration de la connexion. Une fois abonné, un client peut simplement continuer à utiliser cet abonnement pendant de longues périodes. Ils utilisent généralement les ressources informatiques de manière efficace en permettant aux développeurs d'adapter la durée de vie de l'abonnement et de configurer les informations qui seront demandées.

En général, les abonnements permettent au client de souscrire plusieurs abonnements à la fois. En ce qui concerne AWS AppSync, les abonnements ne sont utilisés que pour recevoir des mises à jour en temps réel du AWS AppSync service. Ils ne peuvent pas être utilisés pour effectuer des requêtes ou des mutations.

La principale alternative aux abonnements est le sondage, qui envoie des requêtes à des intervalles définis pour demander des données. Ce processus est généralement moins efficace que les abonnements et met à rude épreuve à la fois le client et le backend.

Une chose qui n'a pas été mentionnée dans notre exemple de schéma est le fait que vos types d'objets spéciaux doivent également être définis dans une schema racine. Ainsi, lorsque vous exportez un schéma au AWS AppSync format, il peut ressembler à ceci :

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

Énumération

Les énumérations, ou énumérations, sont des scalaires spéciaux qui limitent les arguments juridiques qu'un type ou un champ peut avoir. Cela signifie que chaque fois qu'une énumération est définie dans le schéma, le type ou le champ qui lui est associé sera limité aux valeurs de l'énumération. Les énumérations sont sérialisées sous forme de scalaires de chaîne. Notez que les différents langages de programmation peuvent gérer différemment les énumérations GraphQL. Par exemple, n' JavaScript a pas de support d'énumération natif, donc les valeurs d'énumération peuvent être mappées à des valeurs int à la place.

Les énumérations sont définies à l'aide du enum mot clé. Voici un exemple :

enum trafficSignals { solidRed solidYellow solidGreen greenArrowLeft ... }

Lors de l'appel de l'trafficLightsénumération, le ou les arguments ne peuvent être que solidRedsolidYellow,solidGreen, etc. Il est courant d'utiliser des énumérations pour représenter des éléments qui ont un nombre de choix distinct mais limité.

Unions/Interfaces

Voir Interfaces et unions dans GraphQL.