Qu'est-ce que GraphQL ?
November 4, 2022
Qu'est-ce que GraphQL ? Quelle est sa popularité ?
GraphQL est un langage de manipulation de requêtes orienté API, publié par Facebook en 2015. Contrairement à d'autres conceptions d'API, GraphQL permet aux clients de former des requêtes basées sur une structure de données prédéfinie, et permet au serveur de parser la requête pour ne retourner que les données nécessaires. Ainsi, GraphQL offre richesse et flexibilité tout en évitant la perte de performance due aux données redondantes, ce qui en fait un excellent choix pour les applications nécessitant de manipuler de nombreux objets de données complexes.
En 2018, GraphQL a publié une spécification complète et une version stable. La même année, Facebook a fait don du projet GraphQL à la GraphQL Foundation sous l'égide de la Linux Foundation. Depuis lors, GraphQL a été adopté par de nombreux projets open source et organisations commerciales. Aujourd'hui, il existe plusieurs implémentations majeures côté client de GraphQL sur le marché. Les implémentations côté serveur sont disponibles dans tous les principaux langages de programmation serveur, et même dans des langages plus niche comme D et R.
Quelques scénarios réels et défis pour GraphQL
L'exemple le plus connu de GraphQL est l'API GraphQL de GitHub.
Avant d'adopter GraphQL, GitHub fournissait une API REST pour exposer les riches données générées par des millions de projets hébergés, ce qui a été si réussi que cela est devenu un modèle à imiter lors de la conception d'API REST. Cependant, à mesure que le nombre d'objets de données augmentait et que les champs au sein de ces objets devenaient plus volumineux, l'API REST a commencé à révéler de plus en plus d'inconvénients. Côté serveur, GitHub a dû imposer des limites strictes sur la fréquence des appels pour réduire les coûts en raison de la quantité de données générées à chaque appel. Côté développeur, ils ont dû composer avec cette limitation, car bien qu'un seul appel retourne beaucoup de données, la plupart sont inutiles. Pour obtenir une information spécifique, les développeurs doivent souvent lancer plusieurs requêtes, puis écrire beaucoup de code pour assembler les données pertinentes à partir des résultats des requêtes afin d'obtenir le contenu souhaité. Dans ce processus, ils doivent également se conformer aux limites du "nombre d'appels".
Par conséquent, GitHub a adopté GraphQL dès sa sortie. GitHub est devenu l'"ambassadeur" de GraphQL, en faisant la promotion auprès de milliers de développeurs. L'API GraphQL est désormais le premier choix de GitHub. Depuis la première annonce de support pour GraphQL, GitHub a publié plusieurs articles sur GraphQL chaque année. Pour permettre aux développeurs de migrer vers GraphQL, GitHub a écrit une application interactive de requête spécifiquement pour cela : https://docs.github.com/en/graphql/overview/explorer. Les développeurs peuvent apprendre à écrire des requêtes GraphQL via cette application.
Cependant, GraphQL n'est pas une solution miracle. Récemment, GitHub a déprécié sa propre implémentation GraphQL de l'API de packages. Beaucoup de gens ont également commencé à discuter de certains inconvénients de GraphQL. Beaucoup des problèmes de GraphQL proviennent du fait que sa structure est si différente de celle du standard HTTP qu'il n'existe pas de moyen simple de mapper certains concepts de GraphQL dans une structure comme le chemin/header HTTP. Traiter GraphQL comme une API HTTP normale nécessite un travail de développement supplémentaire. Par conséquent, les développeurs qui souhaitent gérer leurs propres API GraphQL devront utiliser une passerelle API compatible avec GraphQL.
Comment APISIX supporte GraphQL
Actuellement, APISIX supporte le routage dynamique via certaines propriétés de GraphQL. Avec cette capacité, nous pouvons accepter uniquement des requêtes GraphQL spécifiques ou faire en sorte que différentes requêtes GraphQL soient redirigées vers différents upstreams.
Prenons l'exemple de la requête GraphQL suivante :
query getRepo {
owner {
name
}
repo {
created
}
}
APISIX extrait les trois propriétés suivantes de GraphQL pour le routage :
- graphql_operation
- graphql_name
- graphql_root_fields
Dans la requête GraphQL ci-dessus :
graphql_operation
correspond àquery
graphql_name
correspond àgetRepo
graphql_root_fields
correspond à["owner", "repo"]
Créons une route pour démontrer les capacités de routage fin de APISIX pour GraphQL :
curl http://127.0.0.1:9180/apisix/admin/routes/1 \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
{
"methods": ["POST"],
"uri": "/graphql",
"vars": [
["graphql_operation", "==", "query"],
["graphql_name", "==", "getRepo"],
["graphql_root_fields", "has", "owner"]
],
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:2022": 1
}
}
}'
Ensuite, utilisons une requête avec une requête GraphQL pour accéder :
curl -i -H 'content-type: application/graphql' \
-X POST http://127.0.0.1:9080/graphql -d '
query getRepo {
owner {
name
}
repo {
created
}
}'
HTTP/1.1 200 OK
...
Nous pouvons voir que la requête a atteint l'upstream car la requête correspondait aux trois conditions.
Inversement, si nous accédons avec une requête qui ne correspond pas, par exemple, le champ owner n'est pas inclus :
curl -i -H 'content-type: application/graphql' \
-X POST http://127.0.0.1:9080/graphql -d '
query getRepo {
repo {
created
}
}'
HTTP/1.1 404 Not Found
...
Elle ne correspondra pas à la règle de routage.
Nous pouvons également créer une route qui permet aux requêtes ne contenant pas le champ owner d'être redirigées vers un autre upstream :
curl http://127.0.0.1:9180/apisix/admin/routes/2 \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
{
"methods": ["POST"],
"uri": "/graphql",
"vars": [
["graphql_operation", "==", "query"],
["graphql_name", "==", "getRepo"],
["graphql_root_fields", "!", "has", "owner"]
],
"upstream": {
"type": "roundrobin",
"nodes": {
"192.168.0.1:2022": 1
}
}
}'
curl -i -H 'content-type: application/graphql' \
-X POST http://127.0.0.1:9080/graphql -d '
query getRepo {
repo {
created
}
}'
HTTP/1.1 200 OK
...
Perspectives du futur support de GraphQL par APISIX
En plus du routage dynamique, APISIX pourrait également introduire plus d'opérations basées sur des champs spécifiques de GraphQL à l'avenir. Par exemple, l'API GraphQL de GitHub a une formule spécifique pour la limitation de débit, et nous pouvons appliquer des règles similaires pour convertir une seule requête GraphQL en un nombre correspondant d'"appels virtuels" pour accomplir une limitation de débit spécifique à GraphQL.
Nous pouvons également envisager le problème d'une autre manière. L'application elle-même continue de fournir l'API REST, et la passerelle convertit les requêtes GraphQL en requêtes REST et les réponses REST en réponses GraphQL au niveau le plus externe. L'API GraphQL fournie de cette manière peut effectuer des fonctions telles que RBAC, limitation de débit, mise en cache, etc., sans développer de plugins spéciaux. D'un point de vue technique, cette idée n'est pas si difficile à mettre en œuvre. Après tout, en 2022, même les API REST ont tendance à fournir des spécifications OpenAPI comme schéma, ce qui n'est qu'une conversion entre le schéma GraphQL et le schéma OpenAPI, plus le filtrage spécifique des champs GraphQL. (Bien sûr, je dois admettre que je ne l'ai pas encore pratiqué moi-même. Peut-être y a-t-il des défis dans certains détails qui restent à surmonter.)
Les lecteurs attentifs remarqueront que les API GraphQL converties de cette manière ne peuvent opérer que sur un seul modèle à la fois, ce qui ne répond évidemment pas aux exigences de flexibilité de GraphQL et n'est rien de plus qu'une API REST habillée en GraphQL. Cependant, GraphQL a un concept appelé schema stitching qui permet aux implémenteurs de combiner plusieurs schémas ensemble.
Par exemple, nous avons deux API, l'une appelée GetEvent et l'autre GetLocation, qui retournent respectivement les types Event et Location.
type Event {
id: string
location_id: string
}
type Location {
id: string
city: string
}
type Query {
GetEvent(id: string): Event
GetLocation(id: string): Location
}
Nous pouvons ajouter une configuration qui combine ces deux API en une nouvelle API appelée GetEventWithLocation, qui ressemble à ceci :
type EventWithLocation {
id: string
location: Location
}
type Query {
GetEventWithLocation(id: string): EventWithLocation
}
L'implémentation spécifique du stitching est réalisée par la passerelle. Dans l'exemple ci-dessus, la passerelle divise l'API en deux, appelant GetEvent pour obtenir le location_id, puis GetLocation pour obtenir les données combinées.
En résumé, en convertissant REST en GraphQL, chaque API REST peut être transformée en un modèle GraphQL correspondant ; et avec l'aide du schema stitching, plusieurs modèles peuvent être combinés en une seule API GraphQL. De cette manière, nous pouvons construire une API GraphQL riche et flexible sur une API REST existante et gérer des plugins spécifiques au niveau de granularité de l'API REST. Cette conception résout incidemment certains problèmes d'orchestration d'API. Comme dans l'exemple ci-dessus, nous prenons la sortie d'une API (Event.location_id) comme entrée d'une autre API (Location.id).