Publié par

Il y a 2 semaines -

Temps de lecture 12 minutes

GraphQL – Depuis les tranchées

Prologue

Novembre 2016.

Dans la salle Eiffel du 7e étage, à la veille d’une des Magic Estimation qui s’annonçait titanesque, composée de stories frôlant la science-fiction, les deux développeurs du projet F avaient commencé à poser les hypothèses techniques sur les éléments fondateurs de la plate-forme. Ils en étaient au protocole d’échange de la donnée entre client et serveur.

Les meilleurs choix, probablement, sont ceux qu’on prend naïvement, avec un brin d’inconscience et beaucoup de témérité :

– Du coup, on part sur du GraphQL ?

– Yup.

Faites entrer GraphQL

Certes, à l’époque GraphQL avait déjà fait ses preuves : créé par Facebook en 2012, il avait rapidement été adopté dans un bon nombre de projets internes. Pourtant, sa publication au grand public datait seulement de septembre 2015, soit 14 mois seulement avant le démarrage de notre projet.

Et si la presse technique était totalement conquise par cet n-ième outillage made in Facebook, il était également vrai qu’aucun collègue des 2 développeurs ne l’avait mis en place dans un projet de grande envergure.

Cela dit, les atouts étaient considérables et ils le sont toujours :

  • Format d’échange JSON
  • Typage du payload de réponse (et support des valeurs optionnelles)
  • Formalisme pour décrire les champs souhaités dans la réponse
  • Schéma de définition de l’API
  • Outillage First Party intégré à la solution pour tester les requêtes…
  • …et inspecter la documentation

Bien sûr, une bonne partie de ces atouts sont familiers aux utilisateurs de Swagger ou à ceux pour qui SOAP n’est pas une demande de propreté auprès d’un collègue londonien.

Mais l’avantage de GraphQL est d’inclure la totalité de ces fonctionnalités, notamment langage, schéma et runtime, dans une seule solution cohérente et complète.

Il serait cependant inexact d’attribuer la popularité de GraphQL et la raison de son choix dans le cadre du projet F, à ses fonctionnalités uniquement : la dernière (et peut être la plus importante) flèche dans le carquois de GraphQL est son degré de proximité avec d’autres technologies largement adoptées dans le domaine du Web et, notamment React et Relay, créées, elles aussi, par Facebook.

En tout cas, même si le projet F ne prévoyait ni React ni Relay, la proposition de valeur était forte. Suffisamment forte pour convaincre les deux développeurs.

Veuillez formuler vos requêtes

D’un point de vue de l’usage, GraphQL permet de réaliser la requête suivante :

// POST /my/api/

query {
  myFriends {
    name
  }
}

À ce POST, le serveur nous répondra avec un payload JSON

{
  "data": {
    "myFriends": [
      { "name": "Farrokh" },
      { "name":  "Brian Harold" }
    ]
  }
}

Si on voulait changer la requête, pour inclure le nickname, rien de plus simple : il suffit juste de POST le suivant

query {
  myFriends {
    name
    nickname
  }
}

Pour recevoir le payload qui suit :

{
  "data": {
    "myFriends": [
      { 
        "name": "Farrokh",
        "nickname": "Freddie"
      },
      { 
        "name":  "Brian Harold",
        "nickname": "Brian"
      }
    ]
  }
}

Le client peut choisir quels champs recevoir, en réduisant donc le couplage entre client et serveur : chaque consommateur de l’API pourra donc bénéficier d’une réponse sur mesure – aspect très pratique lors du développement d’un système composé par des clients distincts, tels que Web et Mobile. Un autre bénéfice, surtout d’un point de vue organisationnel, est une simplification des interactions entre équipes back et front.

Des types bien

Faire coexister des langages au typage plutôt fort (hello Kotlin, Swift, TypeScript) avec des payloads d’échange qui ne le sont pas du tout (hello JSON) est une pratique souvent fastidieuse.

GraphQL résout (en partie) ce problème en fournissant un schéma de l’API, dans lequel chaque élément retourné est conforme à un type. Dans l’exemple précédent, nous avons demandé le champ name de l’objet friend ; en revanche si on avait requis le champ flavor, le runtime GraphQL nous aurait retourné une erreur (sauf au cas où le schéma l’aurait exposé). Qui plus est, dans la plupart des cas, nous n’aurions même pas pu écrire la requête (plus d’info dans la suite de l’article).

Et, si on veut parler code, voici à quoi ça ressemble le schéma de notre API :

type Query {
  myFriends: [Friend]
}

type Friend {
  id: ID!
  name: String
  nickname: String
  friends: [Friend]
}

En plus des queries, illustrées dans l’exemple précédant, GraphQL supporte d’autres méthodes (ou, plus proprement, operations) : il s’agit des opérations d’écriture (mutations) et de souscription aux changements en temps réel (subscriptions). La séparation des operations de lecture et écriture, tout comme le système de types, est un trait distinctif de GraphQL : le système interdira par ailleurs au client d’effectuer en même temps des opérations de lecture et écriture.

+ Frame, – Work

Lors de cet après-midi de magic estimation du projet F, deux autres éléments avaient joué en faveur de la solution : le framework Apollo, capable d’accélérer considérablement la mise en place du système et GraphiQL, une interface visuelle pour expérimenter les queries.

Houston, we (don’t) have a problem

Bien qu’Apollo fournisse désormais des services payants (ce n’était pas le cas en 2016), la presque totalité de ses fonctionnalités sont actuellement Open Source et distribuées gratuitement sous le nom “Community”.

Côté serveur, Apollo fournit des outils pour implémenter les opérations de l’API, ainsi que pour brancher des opérations à des services REST ou à d’autres sources de données existants.

Côté client, le framework met à disposition des développeurs un outil de génération de code. Ce dernier produira automatiquement du code (en Swift ou Kotlin) pour accéder au réseau (en utilisant les APIs iOS ou Android) et désérialiser le payload de réponse en objets métier “safe” dont le code de la définition est également générée par Apollo.

No bullshit here : le code généré par Apollo est propre, correct et facilement intégrable dans un projet de la vraie vie™️.

Ceci ne veut pas dire que l’outillage d’Apollo est parfait : lorsqu’on sort des sentiers battus on se retrouve parfois abandonnés par l’outillage et certains bugs commencent à apparaître. De nombreuses issues sont recensées (souvent sans réponse) sur la page GitHub du projet.

Le grand “i” dans GraphiQL

Outil essentiel à un usage efficace de GraphQL – et vrai game changer – dans le domaine, GraphiQL est l’interface d’expérimentation et de consultation de la documentation des opération.

Publié traditionnellement dans un sous-chemin des environnements de staging (par ex. myapp.com/api/graphiql), GraphiQL est un éditeur en miniature d’opérations : il permet l’écriture de requêtes GraphQL, (avec complétion de code, auto-indentation et  coloration syntaxique), leur exécution, le passage de paramètres personnalisés, ainsi que la consultation en ligne de la documentation. Il devient rapidement un outil incontournable dans le quotidien du développeur front, d’autant plus que les opérations écrites peuvent être copiées telles quelles dans un projet client pour ensuite déléguer à Apollo la génération du code de la requête.

Par rapport aux solutions traditionnelles, et dans le cadre du développement d’applications mobiles, le flux de travail permis par GraphiQL + Apollo est considérablement plus rapide et robuste et apporte une véritable plus value.

There’s no such thing as magic

On a décrit ici un monde idyllique, fait d’outils incroyablement simples et efficaces. Est-ce toute la vérité? Non, enfin, presque.

Il faut bien le rappeler, aucun outil n’est magique, et GraphQL ne fait pas exception. Lors de sa mise en place vous serez confrontés à plusieurs contraintes, notamment liées à sa mise à disposition.

Si on revient à l’essentiel, GraphQL n’est qu’une interface pour consommer la donnée de votre SI. Et la manière dont GraphQL accède à cette donnée est toute une autre histoire. Vous avez un beau vieux monolithe ? Dans ce cas, pas de souci. Votre application est une galaxie de microservices ? L’interface GraphQL sera probablement un service additionnel fournissant un point d’accès unique à l’ensemble des autres APIs. La communication avec ces dernières demandera une certaine attention et rigueur dans l’orchestration pour éviter que votre projet ne tourne au cauchemar.

Rien n’est parfait, toi non plus.

POST & Caching

Mais ce n’est pas tout. Les lecteurs les plus attentifs l’auront compris dès le premier exemple : toutes les opérations envoyées à votre instance GraphQL sont transmises via POST ! Or, cela serait normal lors d’une création ou modification d’objets mais malheureusement c’est le cas même pour les demandes de récupération (celles que l’on aurait traditionnellement exécutées en GET).

Et le cache client ? Sans trop digresser, nous nous limiterons à dire que la problématique est réelle et que les solutions les plus robustes demandent un investissement additionnel en termes d’implémentation, tant côté serveur que côté client.

Si l’on souhaite se simplifier la tâche, le client Apollo fournit un système de caching (Apollo Store) qui se veut solide mais qui est loin de couvrir tous les cas de figure d’un projet de la vraie vie™️.

Une problématique additionnelle amenées par les méthodes POST est la difficulté de positionner un cache ou un CDN (via CloudFront ou Akamai par exemple) pour servir des réponses dans des contextes peu dynamiques (pensez à des données d’un catalogue). Encore une fois, les solutions ne manquent pas, mais elles complexifient inévitablement la mise en place du système.

Un grand pouvoir implique…

Dans une application REST traditionnelle, l’architecte de l’application exposant l’API est souvent le maître des destins des consommateurs du service. Avec GraphQL ce rapport de force, voire de dépendance, est bien évidemment mitigé par le fait que chaque client possède un plus grand degré de liberté dans l’utilisation de l’API.

Ce pouvoir est (Spider-Man FTW) à double tranchant : le développeur du client devra donc prêter attention, par exemple, à la taille des payloads des réponses ainsi qu’à la complexité des opérations, pour éviter des échanges inutiles de données ou, au pire, de surcharger dangereusement l’application back.

Encore plus que dans une application REST traditionnelle, l’usage des APIs exposées via GraphQL devront faire l’objet de reviews additionnels de la part des équipes back et SRE.

La pyramide du DOStin

Revenons à notre exemple initial.

query {
  myFriends {
    name
  }
}

Et si on souhaitait demander les amis de mon ami ? Rien de plus simple.

query {
  myFriends {
    friends
  }
}

Les amis de mon ami étant des Persons, nous pouvons bien évidemment demander leurs amis. Et pour chacun d’entre eux, nous pouvons demander leurs amis. Rien nous empêche, finalement, d’écrire une query comme la suivante :

query {
  myFriends {
    friends {
      friends {
        friends {
          friends {
            friends {
              name
            }
          }
        }
      }
    }
  }
}

Et si l’opération de recherche d’amis était complexe en termes de calcul ? Rien ne vous protégerait contre une des attaques DOS les plus simples de l’histoire.

La littérature sur GraphQL est pleine de bonnes pratiques pour limiter, voire annuler totalement ce risque. Mais le choix de l’approche dépendra de vos besoins et demandera – bien sûr – une implémentation additionnelle.

Pour conclure, configurer un système complexe avec GraphQL est loin d’être un no-brainer et le runtime du langage ne fournit pour le moment aucune solution pré-packagée pour ces problématiques.

Ré-écrivons le futur

Novembre 2016.

Et si GraphQL n’existait pas ou, mieux, ne jouissait pas de la bonne presse qu’il avait à l’époque. Quels choix auraient eu les deux développeurs ?

Simple de le dire aujourd’hui, mais JSON:API l’aurait sûrement remporté contre les APIs REST traditionnelles. À vrai dire, actuellement, JSON:API, est un bon choix tout court. Comme pour beaucoup de technologies, il existe de véritables factions défendant l’une ou l’autre solution même si les deux partagent pour la plupart les mêmes principes.

Par rapport à GraphQL, JSON:API propose un usage plus immédiat, car plus proche de REST, et une mise en place plus rapide dans les cas plus simples.

Épilogue

Avril 2017.

Lors du debrief de fin des développements, à l’autre bout du monde, les deux développeurs tirent le bilan des choix technologiques pris sur le projet F. Et GraphQL est là, parmi les paris gagnants : même avec les quelques difficultés rencontrées, les bénéfices apportés ont été indéniables.

Qui plus est, pendant les 3 ans suivants, chaque projet sur lequel les deux développeurs auront eu l’occasion de travailler, aura contenu du GraphQL. Ceci témoigne l’efficacité et l’utilité de la solution, et de l’intérêt du marché pour des approches post-REST comme le sont GraphQL, le déjà mentionné JSON:API mais surtout gRPC, qui méritera un approfondissement dans un prochain article.

Pour résumer, GraphQL apporte une véritable plus value dans l’exposition d’une API, grâce à son runtime et à son outillage. Sa bonne mise en place dans des environnements de production nécessite quelques réflexions avancées, son adoption ne peut pas être considérée comme un no-brainer et n’est donc pas adaptée à des projets de petite taille. À l’inverse, dans des projets plus ambitieux, l’effort est amplement récompensé par une efficacité grandement accrue pour les équipes.

Les développeurs du projet F vous le confirmeront.

Publié par

Publié par Simone Civetta

Simone est responsable technique des équipes mobilités chez Xebia. Il est également formateur au sein de Xebia Training .

Commentaire

0 réponses pour " GraphQL – Depuis les tranchées "

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Xebian, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.