Qu'est-ce que GraphQL?

Vue d'ensemble

GraphQL est une nouvelle API intéressante pour les requêtes ad hoc et la manipulation. Il est extrêmement flexible et offre de nombreux avantages. Il est particulièrement adapté à l'exposition de données organisées sous forme de graphiques et d'arbres. Facebook a développé GraphQL en 2012 et en source ouverte en 2015. 

Il a rapidement décollé et est devenu l'une des technologies les plus en vogue. De nombreuses entreprises innovantes ont adopté et utilisé GraphQL en production. Dans ce tutoriel, vous apprendrez: 

  • les principes de GraphQL
  • comment il se compare à REST
  • comment concevoir des schémas
  • comment configurer un serveur GraphQL
  • comment implémenter des requêtes et des mutations 
  • et quelques sujets avancés supplémentaires

D'où vient GraphQL Shine?

GraphQL est à son meilleur lorsque vos données sont organisées dans une hiérarchie ou un graphique et que le système frontal souhaite accéder à différents sous-ensembles de cette hiérarchie ou de ce graphique. Considérons une application qui expose la NBA. Vous avez des équipes, des joueurs, des entraîneurs, des championnats et beaucoup d’informations sur chacun. Voici quelques exemples de requêtes:

  • Quels sont les noms des joueurs sur la liste actuelle des Golden State Warriors?
  • Quels sont les noms, hauteurs et âges des partants des Washington Wizards?
  • Quel entraîneur actif a le plus de championnats?
  • Pour quelles équipes et en quelles années l'entraîneur a-t-il remporté ses championnats??
  • Quel joueur a remporté le plus de prix MVP?

Je pourrais venir avec des centaines de telles requêtes. Imaginez que vous deviez concevoir une API pour exposer toutes ces requêtes au serveur frontal et pouvoir facilement étendre l’API avec de nouveaux types de requêtes à mesure que vos utilisateurs ou votre chef de produit proposent de nouveaux éléments intéressants à interroger..

Ce n'est pas anodin. GraphQL a été conçu pour résoudre ce problème précis et, avec un seul noeud final d'API, il fournit une puissance énorme, comme vous le verrez bientôt..

GraphQL vs. REST

Avant de plonger dans les rouages ​​de GraphQL, comparons-le à REST, qui est actuellement le type d'API Web le plus populaire..

REST suit un modèle orienté ressources. Si nos ressources sont des joueurs, des entraîneurs et des équipes, il y aura probablement des paramètres tels que:

  • /joueurs 
  • /joueurs/ 
  • / entraîneurs
  • / entraîneurs / 
  • / équipes
  • / équipes /

Souvent, les noeuds finaux sans identifiant renvoient simplement une liste d'identifiants et les noeuds finaux avec l'identifiant renvoient les informations complètes sur une ressource. Vous pouvez bien entendu concevoir votre API de différentes manières (par exemple, le point de terminaison / players peut également renvoyer le nom de chaque joueur ou toutes les informations relatives à chaque joueur)..

Le problème avec cette approche dans un environnement dynamique est que vous sous-récupérez (par exemple, vous ne récupérez que les identifiants et avez besoin de plus d'informations), ou vous sur-récupérez (par exemple, vous obtenez toutes les informations sur chaque joueur lorsque vous êtes connecté). juste intéressé par le nom). 

Ce sont des problèmes difficiles. En cas de sous-extraction, si vous récupérez 100 identifiants, vous devrez effectuer 100 appels d'API distincts pour obtenir les informations sur chaque lecteur. En cas d'extraction excessive, vous perdez beaucoup de temps en back-end et en bande passante réseau à préparer et à transférer beaucoup de données inutiles.

Il existe des moyens de résoudre ce problème avec REST. Vous pouvez concevoir de nombreux points de terminaison sur mesure, chacun renvoyant exactement les données dont vous avez besoin. Cette solution n'est pas évolutive. Il est difficile de maintenir la cohérence de l'API. C'est difficile à faire évoluer. C'est difficile à documenter et à utiliser. Il est difficile de le maintenir quand il y a beaucoup de chevauchement entre ces points de terminaison sur mesure.

Considérez ces points de terminaison supplémentaires:

  • / joueurs / noms
  • / players / names_and_championships
  • / équipe / partants

Une autre approche consiste à conserver un petit nombre de terminaux génériques, tout en fournissant de nombreux paramètres de requête. Cette solution évite le problème des nombreux points de terminaison, mais elle va à l’encontre du modèle REST, et il est également difficile d’évoluer et de maintenir de manière cohérente..

Vous pourriez dire que GraphQL a adopté cette approche à la limite. Il ne pense pas en termes de ressources bien définies, mais en termes de sous-graphiques de tout le domaine.

Le système de types GraphQL

GraphQL modélise le domaine à l'aide d'un système de types constitué de types et d'attributs. Chaque attribut a un type. Le type d'attribut peut être l'un des types de base fournis par GraphQL comme ID, String et Boolean ou un type défini par l'utilisateur. Les noeuds du graphe sont les types définis par l'utilisateur et les arêtes sont les attributs qui ont des types définis par l'utilisateur.. 

Par exemple, si un type "Joueur" a un attribut "équipe" avec le type "Équipe", cela signifie qu'il existe un bord entre chaque nœud de joueur et un nœud d'équipe. Tous les types sont définis dans un schéma décrivant le modèle d'objet de domaine GraphQL.. 

Voici un schéma très simplifié pour le domaine NBA. Le joueur a un nom, une équipe à laquelle il est le plus associé (oui, je sais que les joueurs passent parfois d’une équipe à l’autre) et le nombre de championnats remportés par le joueur.. 

L’équipe a un nom, un éventail de joueurs et le nombre de championnats remportés par l’équipe..

type Player id: nom de l'ID: String! équipe: équipe! championshipCount: Integer!  type Team id: nom de l'ID: String! joueurs: [Player!]! championshipCount: Integer!  

Il existe également des points d'entrée prédéfinis. Ce sont les requêtes, les mutations et les abonnements. Le serveur frontal communique avec le serveur via les points d’entrée et le personnalise en fonction de ses besoins..

Voici une requête qui renvoie simplement tous les joueurs:

tapez Query allPlayers: [Player!]! 

Le point d'exclamation signifie que la valeur ne peut pas être nulle. Dans le cas du tous les joueurs requête, il peut retourner une liste vide, mais pas null. En outre, cela signifie qu’il ne peut y avoir de lecteur nul dans la liste (car elle contient Player!).

Configurer un serveur GraphQL

Voici un serveur GraphQL à part entière basé sur node-express. Il dispose d'un magasin de données codé en dur en mémoire. Normalement, les données seront dans une base de données ou extraites d'un autre service. Les données sont définies ici (excuses à l'avance si votre équipe ou votre joueur préféré ne les a pas créés):

let data = "allPlayers": "1": "id": "1", "name": "Stephen Curry", "championshipCount": 2, "teamId": "3", "2": "id": "2", "name": "Michael Jordan", "championhipCount": 6, "teamId": "1", "3": "id": "3", "name": "Scottie Pippen", "championshipCount": 6, "teamId": "1", "4": "id": "4", "name": "Magic Johnson", "championshipCount": 5, "ID de l'équipe" ":" 2 "," 5 ": " id ":" 5 "," nom ":" Kobe Bryant "," championshipCount ": 5," teamId ":" 2 "," 6 ": " id ":" 6 "," name ":" Kevin Durant "," championshipCount ": 1," teamId ":" 3 "," allTeams ": " 1 ": " id ":" 1 ", "name": "Chicago Bulls", "championshipCount": 6, "joueurs": [], "2": "id": "2", "name": "Los Angeles Lakers", "championshipCount": 16, "players": [], "3": "id": "3", "name": "Golden State Warriors", "championshipCount": 5, "players": [] 

Les bibliothèques que j'utilise sont:

const express = require ('express'); const graphqlHTTP = require ('express-graphql'); const app = express (); const buildSchema = require ('graphql'); const _ = require ('lodash / core');

C'est le code pour construire le schéma. Notez que j’ai ajouté quelques variables à la tous les joueurs requête racine.

schema = buildSchema ('type Joueur id: Nom de l'ID: String! championhipCount: Int! équipe: Team! type Team id: Nom de l'ID: String! championshipCount: Int! joueurs: [Player!]! type requête allPlayers (offset: Int = 0, limite: Int = -1): [Player!]! ' 

Voici l’essentiel: relier les requêtes et servir les données. le rootValue l'objet peut contenir plusieurs racines. 

Ici, il n'y a que le tous les joueurs. Il extrait le décalage et la limite des arguments, découpe les données de tous les joueurs, puis définit l'équipe sur chaque joueur en fonction de son identifiant. Cela fait de chaque joueur un objet imbriqué.

rootValue = allPlayers: (args) => offset = args ['offset'] limit = args ['limit'] r = _.values ​​(data ["allPlayers"]). slice (offset) if (limit> - 1) r = r.slice (0, Math.min (limite, r.longueur)) _.forEnch (r, (x) => data.allPlayers [x.id] .team = data.allTeams [ x.teamId]) renvoyer r, 

Enfin, voici le graphql noeud final, en passant le schéma et l'objet valeur racine:

app.use ('/ graphql', graphqlHTTP (schema: schema, rootValue: rootValue, graphiql: true)); app.listen (3000); module.exports = app;

Réglage graphiql à vrai nous permet de tester le serveur avec un génial IDE GraphQL intégré au navigateur. Je le recommande fortement pour expérimenter différentes requêtes.

Requêtes ad hoc avec GraphQL

Tout est réglé. Naviguons vers http: // localhost: 3000 / graphql et amusons-nous.

Nous pouvons commencer simplement, avec juste une liste des noms de joueurs:

requête justNames allPlayers name Sortie: "données": "allPlayers": ["nom": "Stephen Curry", "nom": "Michael Jordan", "nom": "Scottie Pippen ", " name ":" Magic Johnson ", " name ":" Kobe Bryant ", " name ":" Kevin Durant "] 

Bien. Nous avons quelques superstars ici. Sans aucun doute. Faisons quelque chose de plus sophistiqué: à partir de l'offset 4, obtenez 2 joueurs. Pour chaque joueur, indiquez leur nom et le nombre de championnats qu’ils ont gagnés, ainsi que le nom de leur équipe et le nombre de championnats remportés par l’équipe..

requête deuxPlayers allPlayers (offset: 4, limite: 2) nom équipeseCounthipCount nomCommodesCours Sortie: "données": "allPlayers": ["nom": "Kobe Bryant", "championnatCount":: 5, "team": "name": "Los Angeles Lakers", "championshipCount": 16, "name": "Kevin Durant", "championshipCount": 1, "team": "name": "Golden State Warriors", "championhipCount": 5] 

Donc, Kobe Bryant a remporté cinq championnats avec les Lakers, qui ont remporté 16 championnats au total. Kevin Durant a remporté un seul championnat avec les Warriors, qui a remporté cinq championnats au total.

Mutations GraphQL

Magic Johnson était un magicien sur le terrain à coup sûr. Mais il n'aurait pas pu le faire sans son copain Kareem Abdul-Jabbar. Ajoutons Kareem à notre base de données. Nous pouvons définir des mutations GraphQL pour effectuer des opérations telles que l'ajout, la mise à jour et la suppression de données de notre graphique..

Premièrement, ajoutons un type de mutation au schéma. Cela ressemble un peu à une signature de fonction:

type Mutation createPlayer (name: String, championshipCount: Int, teamId: String): Player

Ensuite, nous devons l'implémenter et l'ajouter à la valeur racine. L’implémentation prend simplement les paramètres fournis par la requête et ajoute un nouvel objet à la data ['allPlayers']. Il s'assure également de définir l'équipe correctement. Enfin, il renvoie le nouveau joueur.

 createPlayer: (args) => id = (_.values ​​(data ['allPlayers'])). length + 1) .toString () args ['id'] = id args ['team'] = data ['allTeams '] [args [' teamId ']] data [' allPlayers '] [id] = args renvoient des données [' allPlayers '] [id],

Pour ajouter Kareem, nous pouvons invoquer la mutation et interroger le joueur renvoyé:

mutation addKareem createPlayer (nom: "Kareem Abdul-Jabbar", championshipCount: 6, teamId: "2") name. championshipCount team name Sortie: "data": "createPlayer": "name": "Kareem Abdul-Jabbar", "championhipCount": 6, "équipe": "name": "Lakers de Los Angeles" 

Voici un petit secret ténébreux sur les mutations… elles sont en réalité exactement les mêmes que les requêtes. Vous pouvez modifier vos données dans une requête et vous pouvez simplement renvoyer les données d'une mutation. GraphQL ne va pas jeter un coup d'œil dans votre code. Les requêtes et les mutations peuvent prendre des arguments et renvoyer des données. Cela ressemble plus à du sucre syntaxique pour rendre votre schéma plus lisible par l'homme.

Sujets avancés

Abonnements

Les abonnements sont une autre caractéristique majeure de GraphQL. Avec les abonnements, le client peut s'abonner à des événements qui seront déclenchés chaque fois que l'état du serveur sera modifié. Les abonnements ont été introduits à un stade ultérieur et sont mis en œuvre par différents cadres de différentes manières.

Validation

GraphQL vérifie chaque requête ou mutation par rapport au schéma. C'est une grande victoire lorsque les données d'entrée ont une forme complexe. Vous n'avez pas à écrire de code de validation ennuyeux et fragile. GraphQL s'en occupera pour vous. 

Introspection de schéma

Vous pouvez inspecter et interroger le schéma actuel. Cela vous donne des méta-pouvoirs pour découvrir dynamiquement le schéma. Voici une requête qui renvoie tous les noms de types et leur description:

query q __schema types nom description

Conclusion

GraphQL est une nouvelle technologie API passionnante qui offre de nombreux avantages par rapport aux API REST. Il y a une communauté dynamique derrière, sans parler de Facebook. Je prédis que cela deviendra un aliment de base en un rien de temps. Essaie. Tu aimeras.