GraphQL est une alternative aux architectures REST pour la réalisation d’API web. Le langage promu par Facebook a des avantages indéniables : récupération des données utiles uniquement, limitation du nombre de requêtes, typage fort, syntaxe puissante et bien pensée… Cependant, il souffre aussi de défauts souvent sous-estimés parmi lesquels l’incompatibilité avec les mécanismes de cache, de log, de sécurité ou d’auth qui forment la base du stack web d’aujourd’hui, ou la nécessité d'un parser spécifique. De plus, les formats hypermedias modernes s’appuyant sur REST disposent de fonctionnalités très similaires tout en restant compatibles avec les fondements du web. Le framework API Platform, basé sur Symfony, permet de créer très facilement des API REST (JSON-LD, JSON API…) et GraphQL. Après avoir énuméré les avantages et inconvénients des différents formats, nous étudierons au travers de différents cas d’usages quand il est préférable d’utiliser GraphQL, REST ou les 2 en complément.
Bonjour à tous.
J'étais déjà là hier, je vais passer assez vite sur les présentations : Kévin Dunglas Je travaille chez Les-Tilleuls je fais partie de la core team Symfony.
Et j'ai été l'auteur du premier commit de cet outil que je vais vous présenter aujourd'hui (et les formats qui vont avec) Les-Tilleuls c'est toujours la même chose qu'hier. C'est toujours autogéré. Les salaires sont toujours partagés à parts égales et on cherche toujours des gens à Lille et à Paris. N'hésitez pas à nous contacter ! Alors, API Platform qu'est ce que c'est ? Qui parmi vous a déjà utilisé cette solution ? Ok pas mal de gens quand même Alors, API Platform c'est un framework full stack, c'est une notion assez. On va voir que c'est une notion assez importante.
On va surtout parler de la partie PHP aujourd'hui. Mais c'est un framework qui contient des composants, à la fois pour réaliser, des applications côté serveur et côté client qui est API Driven. C'est à dire que le point de pivot, le point central de notre application ça va être une API qui va permettre, on va réaliser une API côté serveur, qui va pouvoir exposer différents formats standards ou ouverts (on va voir la différence juste après) dont les deux pattern les plus populaires du moment (à savoir REST et puis le format GraphQL de Facebook, les deux sont supportés) et ensuite de consommer ces API côté client pour faire : des Progressive Web Apps, sites web plus traditionnels, applications mobiles ou objets connectés Alors dans le package API Plateform, il y a ce composant principal, qui est le composant historique celui qui existe depuis le plus longtemps, qui est plus mature, qui probablement est le plus utilisé et le plus connu C'est d'abord une bibliothèque en PHP qui permet d'exposer très facilement des API web REST et GraphQL.
Plus un bundle pour l'intégrer avec Symfony et une distribution sur laquelle on va revenir, qui contient une application Symfony Flex (avec Symfony 4) et tous les paquets qui sont nécessaires pour créer des API REST ou GraphQL préinstallées C'est aussi un composant un peu moins connu : un générateur de modèle, qui est capable de lire le format ouvert Schema.org sur lequel on va (le vocabulaire ouvert Schema.org) sur lequel on va revenir juste après et puis à partir de ce vocabulaire ouvert qui contient énormément de type contribuer par pas mal de gros industriels. Par exemple et un modèle de données très précis pour les véhicules et les voitures qui étaient contribué par exemple par un volkswagen il y a un modèle de données très précis pour les documents qu'on peut publier, les livres etc qui est un changement de Dublin Core qui est fait par des universitaires donc on peut capitaliser sur ses modèles préexistants et générer un modèle de données PHP : donc un ensemble de classe avec les propriétés, les guetters les setters, la PHP doc, les règles de validation pour Symfony, le mapping avec l'ORM Doctrine, éventuellement le mapping avec API Platform.
C'est un composant qui est indépendant en fait, chacun de ses composants est complètement indépendant et peut être utilisé l'un sans l'autre.
Donc si vous voulez générer un modèle de données à partir de quelque chose qui existe déjà dans une application PHP traditionnelle qui n'utilisent pas API Plateform, ce composant va quand même fonctionner Ensuite on a deux composants qui sont faits en Javascript.
Et d'ailleurs c'est assez rigolo, on est au PHP Tour, mais depuis quelques mois Github à requalifier le projet API Plateform et le qualifie de projet Javascript ! Ça fait plaisir ou ça fait pas plaisir ça dépend des développeurs.
Du coup on a deux composants qui sont assez intéressants : Un composant d'admin. Si vous avez déjà utilisé ici EasyAdmin ou Sonata visuellement ça ressemble sauf que c'est du Material Design et sauf que c'est un client qui est complètement dynamique est complètement générique qui va utiliser les capacités d'auto découvrabilité des API modernes REST ou GraphQL pour juste en ayant l'url de l'API être capable de dynamiquement construire une interface d'administration qui ressemble à Sonata pour cette API sans avoir à connaître rien au préalable des types et des opérations qui exposent cette API c'est complètement dynamique complètement auto découvrable grâce à ces nouveaux formats.
Et cet outil : le client générateur, c'est un outil en ligne de commande qui est écrit en Node.js qui va pareil, lire les documentations moderne de d'API qui peuvent être exposées par le composant serveur d'API Platform mais aussi par tout un tas d'outils qui respecte ces formats ouverts, qui peuvent être faits en Python, en Java, en un tout un tas de langages de programmation et qui va générer pour cette API, une Progressive Web App, où visuellement ça va ressembler à ce que génère le MakerBundle de Symfony ou Laravel donc quelque chose une application en CRUD avec une page de liste, une page de detail, une page d'édition, une page de suppression, etc à la différence que comme l'admin, tout va passer via l'API REST, ce sera un projet API Driven. Il n'y aura jamais d'accès direct n'aura jamais d'accès direct qui va être effectué à votre base de données ou à votre code PHP, tout va passer par l'API via les formats ouverts qui sont exposés par l'API et ces deux composants. Ce qui est vraiment important c'est que ça marche, tant qu'on a une API qui supporte JSON-LD et bientôt Swagger Swagger OpenAPI et ça marche quel que soit le langage de programmation côté serveur.
À l'inverse le composant côté serveur, marche quel que soit le langage de programmation côté client.
Que ce soit Javascript, Python, Go ou Swift, Kotlin, ce genre de chose.
Pour les outils, pour cet outil Javascript il y a le support de deux frameworks Javascript (bien sûr on a réinventé la roue ni pour la partie serveur ni la partie client) on peut générer une Progressive Web App qui utilise React et Redux, ou depuis assez peu on peut aussi générer une Progressive Web App qui supporte Vue.js.
Vous pouvez choisir votre framework préféré. En fait on peut ajouter des squelettes pour supporter d'autres formats.
Il y a un squelette expérimental qui permet de générer une app React Native qui peut être packagé sur l'Apple Store, iOS ou sur le Google PlayStore Alors par défaut le composant serveur dont on va parler le plus aujourd'hui, supporte tout un tas de formats.
Si vous êtes venu à ma conf d'hier sur le serializer Symfony, vous avez vu qu'il y a plusieurs formats qui sont supportés nativement dans le serializer de Symfony. En fait API Platform est basé essentiellement sur le serializer de Symfony et vient lui apporter des capacités dite hypermédia. On va voir un peu plus en détails après en quoi ça consiste mais l'idée principale, c'est que chaque objet PHP va pouvoir être converti en JSON (c'est ce que fait le serializer de Symfony) mais aussi en une url.
On va pouvoir un match direct, 1 pour 1. Chaque objet est identifié par une url publique à laquelle on peut accéder et ça c'est ce qu'apporte ces formats hypermédia là et c'est ce qu'on va c'est ce qu'apporte l'API Plateform par rapport au simple serializer de Symfony.
Alors depuis Décembre dans la je crois que ouai depuis Décembre, dans la version stable d'API Platform il y a aussi le support d'un format qui n'est pas REST il s'appelle GraphQL. Et c'est celui dont on va parler avec REST le plus aujourd'hui.
Alors ça a été un gros boulot quand même pour rajouter ça dans API Plateform et c'était possible à la conception de base du framework.
C'est principalement Alan Poulain, que je tiens à remercier pour l'énorme boulot qu'il a fait sur sur GraphQL qui a ajouté ce support.
Il y a quand même plus de 4500 il y a à peu près 4500 lignes de code ont été ajoutées juste pour ça alors qu'on a pu réutiliser quasiment toute l'infrastructure qui étaient présentes pour REST dans API Platform. Donc il y a quand même pas mal de choses très spécifiques à GraphQL.
Et ensuite on va voir que GraphQL c'est un format qui est un petit peu compliqué qu'il y a des extensions officielles.
Et du coup dans un API Platform maintenant il y a le support du format GraphQL de base mais aussi de toutes ses extensions dont les extensions Relay qu'on va présenter juste après.
Alors comment ça marche ? Comment ça s'installe ? Il n'y a pas besoin d'avoir un diplôme d'ingénieur ! On a des clients qui sont même pas développeur qui arrivent à s'en servir, normalement jusqu'ici, ça devrait aller pour tout le monde.
On va sur Github. On va sur le site on télécharge l'archive.
On le décompresse.
Il faut Docker. La seule dépendance c'est Docker sur la machine. Pas besoin de Composer, pas besoin de PHP, pas besoin de rien du tout.
Pas besoin de serveurs PostgreSQL. On tape : "docker-compose up" et puis on a tout ce qu'il faut on a une petite API de test qui est disponible avec un client, une interface d'admin et on va tout ce qu'il faut pour commencer à développer.
On va pouvoir aussi utiliser ce setup Docker qui est fourni avec tous les conteneurs qui vont bien pour le développement et pour la production pour déployer en quelques minutes une application complète API Platform sur les plateformes de cloud principales : Google Google Cloud Plateform, Azure et Amazon ECS grâce au support de Kubernetes qui est natif.
Alors quand on lance, on a cette page, qui est faite en React et dans laquelle, dans ce conteneur là, on va pouvoir générer la Progressive Web App. Si vous avez le talk d'hier sur les Progressive Web App, c'est votre super SPA qui est faites en JS en HTML, en CSS mais que vous allez pouvoir faire fonctionner comme une app natives sur les téléphones mobiles qui va fonctionner offline, quand ya pas de réseau, qui va être capable de reprendre la connexion, qui utilise les nouvelles API HTML5 et ES6 ES7 pour pouvoir faire des choses beaucoup plus avancées que ce qu'on pouvait faire avant.
Vous allez pouvoir la générer dans ce conteneur là. Vous avez une petite page de doc et vous avez aussi, une API qui est fonctionnelle avec une petite avec un petit endpoint de test (que bien sûr vous allez supprimer quand vous allez commencer à vraiment développer), une documentation Swagger (entre autres) qui est automatiquement générée et Swagger UI qui est intégré avec un petit thème particulier pour avoir une documentation qui aussi lisible par les humains et pas juste un format JSON.
Et puis finalement on a un dernier conteneurs qui est l'interface d'administration (dont je parlais juste avant) qui est dynamique et qui là, par exemple, est complètement dynamiquement généré côté client pour l'API de test qui est pré-fournit dans le conteneur.
Donc on va changer, on va pas s'attarder sur la partie client. Mais sachez que ça marche plutôt pas mal.
Ça c'est basé sur (ceux qui font un peu de JS) la bibliothèque admin-on-rest qui est en train d'être renommé react-admin qui est faites par une boîte française qui s'appelle Marmelab, la boîte de François Zaninotto. Donc tout est basé sur cette bibliothèque.
Du coup, là dedans, vous avez tous les composants clients comme serveur d'API Platform. Vous avez les setup Docker qui vont bien.
Alors petite piste intéressante, si jamais vous faites du Symfony sans API Platform : vous pouvez juste cloner API Platform, copier-coller le dossier qui s'appelle docker et le setup va marcher pour n'importe quel projet Symfony 4.
Il est testé et tout ça donc c'est plutôt pratique.
Vous avez un serveur PostgreSQL qui est natif. On a choisi PostgreSQL mais en fait on peut utiliser tout serveur supporté par Doctrine Vous avez un mécanisme de cache sur lequel je vais revenir qui utilise Varnish qui est préinstallé dans les conteneurs qui sera désactivé si vous installez API Platform sans passer par la distribution.
Vous avez le support d'HTTPS et HTTP2 en dév, ce qui est pratique pour le développement JS parce que certaines fonctionnalités ne sont accessibles qu'en HTTPS et en HTTP2.
Et vous avez le déploiement facile sur Kubernetes dont vous avez parlé juste avant.
Alors si vous voulez pas embarquer docker et tout le stack JS, etc, vous voulais juste créer une petite API, vous avez une autre solution : vous installez Symfony 4, vous prenez la version minimaliste, le squelette, pas le website, vous tapez : "composer req api" c'est une intégration officielle avec les recipes Symfony et vous obtenez la partie serveur d'API Platform minimaliste avec le moins de dépendance possible mais assez pour que ça fonctionne super facilement, qui est disponible vous lancez le serveur web PHP intégré et tout ce qu'on va voir là vous allez pouvoir le faire aussi Là c'est quand on installe juste le squelette Symfony et qu'on lance API Plateform donc là il n'ya plus le truc de test il n'y a plus HTTPS, il n'y a plus HTTP2 un paire de choses qui ont été supprimés mais c'est juste du pur PHP et c'est plus proche de ce qu'on a l'habitude de voir quand on fait du développement Symfony.
Alors par défaut, API Platform n'a qu'un seul format d'activé, c'est JSON-LD.
Si on veut le support de GraphQL, il va falloir mettre un peu plus les mains dans le cambouis, vous allez voir on va avoir quelques gouttes de sueur, il va falloir installer le support de GraphQL. Alors GraphQL, on va voir juste après c'est un format qui est spécifique, il n'est pas du JSON, qui n'est pas du XML, qui ressemble un peu à du JSON mais qui a un format particulier qui nécessite un parser particulier. On s'est pas amusé à le re-développer, on a pris une bibliothèque qui marche bien et qui permet de faire ça est un peu plus. Et c'est celle-là qu'on a utilisé dans API Platform, c'est aussi celle là qui est utilisé dans overblog/GraphQLBundle du coup pour activer le support GraphQL d'API Platform, il faut l'installer en plus, il n'est pas là par défaut et c'est là que les choses se corsent...
Bon en fait pas vraiment, vous faites juste ça et c'est bon vous savez être votre API GraphQL qui va fonctionner pour l'API de démonstration.
Vous avez voilà une API GraphQL qui marche, un endpoint qui s'appelle "/graphql", et une interface graphique qui s'appelle : GraphiQL qui vous permet, à la manière de Swagger UI, de découvrir la documentation de votre API GraphQL et puis de lancer des requêtes de voir les réponses instantanément.
Même GraphQL finalement c'est pas si compliqué que ça.
Alors on a vu une première requête GraphQL, on va voir une première capacité assez intéressante de GraphQL.
Donc vous voyez que c'est un langage qui ressemble à un langage de description de requête, qui ressemble à du JSON mais qui n'est pas du JSON valide.
Mais par contre qui est extrêmement élégant, extrêmement explicite. Là ce que fait cette requête dans une seule requête HTTP, on embarque deux queries GraphQL l'une pour requêter mon endpoint "greeting" et pour récupérer l'objet qui a l'ID 1, "/greetings/1" pour être exacte et de cet objet, c'est le client qui va des qui va dicter les données qu'il veut récupérer et en l'occurrence on veut juste récupérer la propriété name de cette ressource.
On récupère un deuxième. Donc première capacité très intéressante : la possibilité de mettre autant de requêtes que l'on souhaite dans une seule requête HTTP En réponse le serveur GraphQL nous renvoie du JSON.
C'est une chose qui est un petit peu différent de REST, on n'a pas une parité entre ce que l'on envoie et ce que l'on récupère. On a toujours une clé "data" est dispo ici et puis ensuite on a la réponse à notre requête. Donc là on a la propriété Hello de notre objet "/greetings/1", la propriété name pardon, et la propriété name de notre objet notre ressource "/greetings/3".
Alors ça a l'air sympa, mais il ya quand même une question qu'on peut se poser : pourquoi ? Qui parmi vous a déjà utilisé GraphQL ? Ok ça commence à être assez populaire. Qui a déjà fait des API Rest ? Vraiment REST ? Un peu moins ok.
On va reparler un petit peu plus en détails. Et qui a déjà eu des problèmes avec ses API REST ? Ok bah du coup pourquoi GraphQL ? GraphQL c'est le REST en mieux ! REST 2.0 est là et ça s'appelle GraphQL ! Bon ben ça avait l'air d'être pas si terrible que ça, un petit peu vieillot REST parce que GraphQL s'est carrément le REST killer Ou alors celui là c'est mon préféré : REST API va REST-in-peace API. Ok donc a priori une fois qu'on a GraphQL y avait une vraie bonne raison et GraphQL va tuer REST. En tout cas c'est l'énorme buzz qui a eu autour de cette technologie lancée par Facebook du coup on s'y intéresse. On s'est dit : il faut qu'on ait le support de ça parce que ça a l'air vraiment top ! ! Spoiler ! on va voir qu'effectivement il y a des parties intéressantes, que c'est peut-être un petit peu surestimé et REST ( le vrai REST ), peut être un peu sous-estimé aussi. Enfin ça c'est une opinion personnelle.
Alors qu'est ce que c'est que GraphQL ? C'est un langage de requêtage de données. Ça c'est le truc le plus important GraphQL c'est accessoirement un format d'API, mais c'est avant tout un format de requêtage pour des données qui sont exposés à l'externe. Donc finalement c'est un genre de super SQL dédiée aux API web dédié pour fonctionner au dessus d'un layer réseau, au dessus d'internet et plus spécifiquement au dessus d'HTTP, même si ça va pas tirer parti des capacités du protocole HTTP.
Donc c'est spécifiquement fait pour les API web c'est conçu effectivement, et marketé comme une alternative à REST. C'est un format qui est ouvert avec une spécification ouverte publique faite par Facebook, mais ce n'est pas un standard. Ça va être important, c'est vraiment un truc qui appartient Facebook ils ont laissé l'accès à la communauté on peut proposer des contributions mais c'est géré et piloté uniquement par Facebook.
C'est super bien pour un truc les gateway, les passerelles d'API, on verra ça un petit peu à la fin si on a le temps ça date de 2015.
Et contrairement à REST qui est un format d'API qui est orienté ressource, là c'est un système qui est orienté service. C'est à dire que ça va plutôt ressemblé à SOAP dans le concept on va plutôt faire de l'appel de procédures distante et récupérer les récupérer les données qui corresponde contrairement à REST on va plutôt interagir avec HTTP sur des ressources qui sont exposés dont il y a une différence assez fondamentale dans la conception. On va voir du coup, les deux peuvent être aussi plutôt complémentaires.
Alors comment ça marche ? Gros intérêt GraphQL, c'est que ça permet aux clients de demander que les données dont il a besoin. Donc c'est réglé le problème de l'over-fetching. On va voir ça un peu plus en détail.
Mais de récupérer en une seule requête toutes les données dont il a besoin.
Donc on peut imaginer une appli mobile où on a besoin d'un utilisateur, des derniers articles les plus populaires, écrire des produits sur lesquels je suis en promotion. Je peux en une requête récupérer tout ça.
Ce qui est plus difficile à REST et c'est vraiment le gros intérêt de GraphQL. Le client décrit ce dont il a besoin.
Voilà c'est un genre de patron. On envoie cette requête au serveur, le serveur renvoi exactement ce que j'ai demandé.
C'est clair ? Alors, ça ça vient du site GraphQL, qui est très très bien fait la documentation et le site sont vraiment hyper bien foutu je vous invite à aller lire. D'abord on va décrire l'API que les types de données et la pays que l'on expose ensuite on va pouvoir interroger cette API dont on a décrit le schéma ici avec le langage de requêtage GraphQL.
Et puis on a récupéré du JSON en sortie c'est vraiment le principe de base de GraphQL.
Alors dans la spec, on a différentes fonctionnalités principales, on a la gestion des requêtes ( on a vu assez en détail), on a la gestion des mutations.
Alors c'est ce qui va nous permettre de changer les états sur le serveur. On peut dire c'est le support de l'écriture.
En fait ça va vraiment être orienté appel de procédures distantes, on va pouvoir exécuter du code à l'extérieur par exemple passer une commande mettre à jour tel utilisateur ou ce genre de choses on a quelque chose qui est expérimental dans la spec s'est pas encore complètement dedans mais qui est génial, (ma partie préférée de GraphQL à titre perso) c'est les Subscriptions l'idée c'est que vous allez pouvoir requêter des données depuis le serveur et dire au serveur quand ces données vont être modifiés par n'importe quel autre client, je veux être notifié en push et du coup votre serveur va être capable quand votre panier a été modifié, par exemple, par votre appli mobile alors que vous êtes sûr bureau de pousser vers le client les modifications en temps réel et instantanément.
Ça c'est vraiment génial en termes d'expérience utilisateur, ce qui veut dire que, quel que soit le canal avec lequel j'interagis avec mon API, tous les clients sont mis à jour en temps réel et en push.
Le seul problème, c'est que dans l'implémentation la plus populaire qui est Apollo, ça utilise Websocket et du coup ça peut pas marcher ou très difficilement, avec les trucs expérimentaux ( genre Ratchet et tout ça en PHP). Du coup vous fera plutôt passé vers du Node.js, du Go, ou ce genre de langage.
Ensuite on a un système de type et un mécanisme d'introspection. Donc c'est un peu similaire à l'API de réflexion de PHP (on va voir ça juste après).
On a une spécification pour les erreurs qui est assez limité mais qui fait le job et la possibilité qu'on a déjà vu de paralléliser au maximum et d'envoyer plusieurs requêtes GraphQL dans une seule requête HTTP.
(HTTP ou pas d'ailleurs) Ensuite on a des extensions.
Ça c'était ce qui est présent juste dans la spec GraphQL et on a une expansion très populaire à GraphQL qui s'appelle : la Relay GraphQL Specification. Relay en fait c'est une bibliothèque React pour Facebook, qui est faite par Facebook, qui est un client pour GraphQL qui est très très populaire et très très pratique.
En fait, eux, pour que ce client puisse marcher, il faut que le serveur suive un formalisme un peu plus stricte encore.
Du coup, on a une notion de Node. C'est comme le type Object en Java. C'est à dire que c'est un super objet qui étend, tous les types d'une API GraphQL, sont tous du type Node. Du coup on a un pattern qui va avec l'Object Identification : l'ID d'un objet GraphQL il est forcément unique.
Ça peut pas être 1, ça plutôt être un UUID, une URI (on va voir ça après).
Comme ça quand on requête mon objet 1 2 3 4 sur le type Node, même si c'est un objet de type User ou un objet de type Panier, on va toujours le récupérer. Ça c'est plutôt pratique et plutôt bien pensé.
Ensuite on a une structure dédiée pour la gestion de la pagination (on va voir après) et ont un format dédié pour les mutations orientées CRUD (on va voir aussi).
Tout ça, c'est supporté de base dans API Platform. Vous n'avez pas besoin de le réimplémenter.
Il y a quelques gros acteurs qui sont déjà passés à GraphQL. C'est le cas de Github.
Un exemple un peu plus complet qui suis à la fois GraphQL et Relay : une requête vers l'API de Github.
Je tape mon type Organisation, je passe en paramètre un login.
Je veux récupérer toutes les contributions récentes, publiques des coopérateurs des Tilleuls.
Sur l'organisation, je veux uniquement les champs ID et name de cette organisation.
Ensuite je peux naviguer dans les relations de la manière que je souhaite et demander exactement ce que je veux comme données.
Là, je veux la liste des employés des Tilleuls, les 100 premiers.
C'est une collection donc là j'ai la structure de données imposée par Relay pour les pagination. Pour chaque noeud de ma collection, donc pour chaque employé, je veux l'ID, le login, le nom. Ensuite je veux la liste des contributions de cet employé et quelques paramètres et pour chacune de ces contributions des repositories Github auxquels on a contribué, je veux son ID et son nom.
Je dis exactement ce que je veux (c'est une requête un peu complexe), et juste en envoyant ça, en une seule requête, j'ai ce que je souhaite et je peux afficher ce que je sais.
On a vraiment fait le tour de GraphQL. Alors plus simple, peut être pas...
REST, qu'est ce que c'est ? C'est une thèse de Roy Fielding et c'est un style architectural, contrairement à GraphQL qui est un format spécifié.
C'est quelque chose qui tire parti au maximum des capacités du protocole HTTP.
REST ça a été repris, entre autres, par l'API Twitter, la première appli Facebook comme un terme marketing qui ne respectent absolument pas les concepts qui étaient définis dans la thèse de Fielding.
Du coup il y a une petite confusion entre ce qu'on appelle REST et ce qui est vraiment REST.
Les vrais API REST sont dites hypermédia.
Un autre terme est employé pour lever cette confusion "marketing" c'est HATEOAS.
REST, contrairement à GraphQL et contrairement à un SOAP, c'est un protocole qui est orienté ressource.
Pendant très longtemps c'était un peu la jungle sur comment faire des vrais API REST hypermédia.
On avait les concepts, mais on n'avait pas les formats.
Heureusement, depuis quelques années, on a maintenant des formats et on a des normes, qui spécifient précisément comment devrait se comporter une API REST hypermédia auto-découvrable.
La plupart sont des standards qui sont établies par le W3C. C'est à dire le même organisme qui standardise comment fonctionne le web ouvert, HTML (même si maintenant il y a WhatWG) HTML, CSS, XML, RDF.
Tous ces formats qui sont les fondements du web ouvert, sont définis par le W3C et implémentés par tous les navigateurs et les développeurs.
Maintenant on a des formats similaire pour les API REST.
Le premier c'est JSON-LD qui a été contribué par Google en 2014 au W3C.
C'est juste du JSON, avec des métadonnées qui permettent d'ajouter le support de type complexe et qui permettent d'ajouter des capacités hypermédia (donc gestion de liens entre les API). Je vais pas rentrer trop dans le détail là dedans j'ai déjà donné des confs dessus.
Vous y repasserez si ça vous intéresse en détail.
On a aussi Schema.org dont j'ai parlé pour le générateur donc c'est un vocabulaire ouvert.
Pas vraiment une spécification en tant que tel, c'est plutôt un ensemble de type qui permettent d'exposer avec exactement les mêmes noms et les mêmes types, le même type de données avec différentes API. Par exemple, c'est grâce à ça que les Rich Snippets de Google fonctionnent.
Vous avez à rajouter du balisage Schema.org sur vos sites internet vous dit voilà ça c'est : un événement, le nom de l'événement c'est ça, le prix de la place c'est ça, on achète le ticket à tel endroit, en suivant la spécification, les noms fournis par Schema.org et ça marche.
En fait ça été fait également pour pouvoir exposer des API qui sont publiques.
L'idée, si j'ai cinq sites qui expose une API où je peux récupérer des tickets d'événements, s'ils suivent tous cette convention de nommage là avec le format JSON-LD (qui est le format préféré pour Schema.org depuis l'automne dernier), je vais avoir besoin d'un seul client générique qui va être capable de consommer toutes ces API sans même avoir à lire la doc spécifique de cette API.
C'est vraiment ça l'enjeu d'hypermédia, de REST, du web ouvert.
Et finalement, j'ai à Hydra, qui est une extension de JSON-LD, qui est fait par le même groupe de travail par les mêmes gens chez Google etc, et qui là, ajoute à JSON-LD (qui est un peu en lecture seule) tout ce qu'il faut.
C'est un genre de super Swagger pour JSON-LD.
Ça permet de décrire tous les types qu'expose mon API, toutes les opérations qui sont disponibles sur mon API. Est-ce que mes propriétés sont en lecture ? En écriture ? etc Donc en parsant ça, on peut connaître l'ensemble de la structure d'une API.
C'est la même chose (très très proche) que le schéma GraphQL.
Il y a beaucoup de similarités entre les deux.
Il y a d'autres gros acteurs qui utilisent ces formats là.
Il y a la BBC entre autres. Et l'exemple le plus connu c'est les nouvelles API Google dont celle ci.
C'est du JSON, ça ressemble à du REST. Vous voyez que là les requêtes on les passe directement en paramètres d'url, comme on a toujours fait. C'est un document qui est JSON sauf qu'il y a certaines clés qui commence par un @ qui sont réservées, qui sont définies dans JSON-LD, et qui permettent de donner des métadonnées assez avancés. Par exemple, les types précis.
Parce que dans JSON, il n'y a pas de type complexe. Là dedans JSON-LD il y a des titres complexes. Et le mapping avec le vocabulaire Schema.org. Par exemple, là je sais que la description ça correspond à Schema.org/description.
On va en voir un petit peu plus après.
On a plein de format alternatif qui sont tous aussi bien les uns que les autres.
Par exemple json:api et OpenAPI.
Tous sont très intéressants, mais ne sont pas des standards comme GraphQL.
Rentrons un peu plus dans le vif du sujet. Créons notre propre API avec API Platform.
J'ai un quart d'heure pour vous faire découvrir toutes les fonctionnalités et comment faire une API REST hypermédia qui suit tous ces formats super compliqués.
Et une API GraphQL, qui suit tous ces formats super compliqué aussi. Mais je pense que ça va le faire.
C'est assez facile.
Vous commencez par API Plateform, c'est un framework orienté API Design First.
Vous commencez par concevoir ce que va consommer votre client d'API.
Qu'est-ce que je dois exposées dans mon JSON public ? Et ça vous le modéliser sous forme de classe PHP bête et méchante.
Je veux exposer un document Author, qui a une propriété ID et qui a une propriété name. C'est ce que je fais là.
Je rajoute quelques métadonnées.
Obligatoirement je met l'annotation "@ApiResource", je mets ma PHPDoc. Et ça suffit pour avoir une API qui fonctionne.
Sans le support de la lecture et de l'écriture, la persistance en base de données. Si je veux la persistance soit j'ai deux interfaces implémenter moi-même.
Et j'utilise de l'Event Driven, du CQRS. Je ne suis pas obligé d'avoir le même modèle de données internes que je vais persister et que le modèle de donnée public.
Soit je suis en mode R&D, je veux juste faire un prototype ou développer assez vite et du coup je peux juste il y a une intégration optionnelle avec doctrine et je peux juste mapper mon entité publique avec mon modèle de données internes. c'est la même classe Mais ce n'est pas obligatoire et c'est pas forcément ce qu'il faut faire ça dépend des cas.
Si je fais ça j'ai la persistance qui marche de base.
Juste avec ça, j'ai une API REST complète avec tout un tas de fonctionnalités (qu'on va voir) qui sont disponibles.
Là, vu que c'est l'hypermédia, j'ajoute une relation, donc mes auteurs sont liés à des livres.
Pareil, j'ai pas grand chose de plus à faire. Cette annotation là "@ApiProperty", elle est optionnelle.
Elle permet juste de faire le mapping avec Schema.org si je le souhaite.
Si vous le mettez pas, ça marchera quand même mais je n'aurais pas le mapping avec Schema.org.
Je peux aussi rajouter des assertions de validation avec le composant Symfony Validator et du coup la validation va être automatiquement prises en compte.
Pour une API basique, (il y a un peu à coder si vous voulez faire des trucs spécifique), il n'y a rien de plus que ça à faire. À la limite, il n'y a pas besoin d'être développeur pour le faire.
Une fois que j'ai fait ça...
J'ai une API GraphQL, juste en créant ces classes là dans mon squelette, j'ai une API GraphQL pour ces deux types, qui est disponible.
Et j'ai accès aux fonctionnalités d'introspection donc j'ai ce type spécial __schema, qui permet aux clients de demander quels sont les types exposé par l'API et quelles sont les opérations que je peux appliquer dessus (les queries et les mutations).
J'ai aussi, dans le petit outil qui est fournie GraphiQL, la possibilité de naviguer dans cette documentation est de savoir quels sont les queries, les mutations, quels sont les types.
Et la PHPDoc est extraites automatiquement. Et avoir une documentation qui est lisible par les humains.
Ça marche tout seul.
En fait dans API Platform, les classes sont parsés par un mécanisme spécial qui utilise quelques composants Symfony (dont PropertyInfo), vont être stockés dans une représentation intermédiaire.
Donc j'ai des métadonnées dans API Plateform qui décrivent tous les types que j'expose, tous les documents toutes les propriétés de ces documents toutes les relations entre ces documents.
Est-ce que c'est read-only ou pas ? Quelle est la description etc Et que ce soit l'API GraphQL, les API REST, les documentations Swagger, etc, tout est généré à partir ces métadonnées.
C'est pour ça que quand on veut rajouter un nouveau format, c'est assez facile parce qu'on a déjà la représentation intermédiaire, il reste plus qu'à faire le normalizer Symfony, comme on a vu hier, qui va supporter le format qu'on veut ajouter.
Avec ce schéma GraphQL vous êtes capable de faire des clients qu'on dit "intelligents", qui vont être capables, tant que l'API respecte la spec GraphQL, de découvrir ce qui est disponible et de marcher avec toute API, quel que soit la business logic, le business model qui est définie derrière, le client, l'infrastructure va fonctionner de base, sans avoir à la recoder, de projet en projet.
Et ça c'est génial ! Sauf que ce n'est pas spécifique à GraphQL et qu'on sait le faire depuis longtemps avec REST.
Et que c'est vendu comme un "avant" de GraphQL, alors que, Hydra dont on a parlé tout à l'heure, ça permet de faire exactement la même chose : d'exposer toute la structure de notre API pour que des clients intelligents soient capables de les découvrir.
Et API Plateform supporte les deux.
Bien sûr c'est pas si simple à coder. Mais une API GraphQL n'est pas si simple non plus.
On a un document JSON ici, cette fois ci, contrairement à GraphQL où le requêtage n'est pas JSON, qui décrit toutes les propriétés tout les types toutes les classes exactement comme le schéma GraphQL. Les différences entre les deux existent, bien sûr, différences d'implémentations, mais conceptuellement c'est quand même très très proche.
Du coup, côté client ça permet de faire exactement la même chose à la différence que contrairement à GraphQL, c'est de l'API hypermédia.
Ce qui signifie, que c'est fait pour que différentes API puissent collaborer ensemble. Alors que les API GraphQL c'est plutôt en silo : je requête l'API Facebook, ou l'API Github, faire en sorte que mon client puisse passer de l'API Facebook à l'API Github, dans la même application c'est pas trop fait pour ça.
Les API hypermédia c'est fait pour ça.
Il y a plein de compatibilité avec des trucs beaucoup plus avancés, dont les formats RDF du W3C (j'en parlent pas pour aujourd'hui).
Grâce à ce format de documentation ouvert, c'est comme ça que le générateur d'admin fonctionne et le générateur d'admin est capable de découvrir pour cette entité toutes les entités qui sont disponibles et tous les types et même de régler les champs HTML qui correspondent : un select, un champ datepicker, etc C'est parce que toutes les informations qui sont nécessaires sont décrits à l'avance.
Pour récupérer des données, maintenant, en REST, vous devez avoir un peu l'habitude de ça : je récupère mes données via l'url, j'ai une relation de mon livre vers mon auteur, et dois faire une deuxième requête HTTP. Et ça c'est embêtant ça peut, surtout quand on n'est pas HTTP2, de causer des problèmes de performances pour récupérer les infos de l'auteur.
Ça, il y a un nom. C'est une problématique bien connue. Si j'ai une liste, je vais devoir faire une requête par élément de ma liste + la requête pour ma liste = 11 requêtes pour afficher ma liste.
C'est pas terrible en termes de performances. C'est le problème de l'under-fetching.
En GraphQL, c'est vachement mieux.
Je dis tout de suite que je veux un book, son titre, un auteur, son nom et en une seule requête, j'obtiens tout ce qui me faut.
Ça c'est génial, c'est déclaratif, c'est élégant, c'est beaucoup plus clair que ce que j'ai passé dans l'url. J'ai une seule requête.
Ça règle les problèmes d'under-fetching et d'over-fetching.
Par contre, il faut un parser spécifique côté serveur. Bon là c'est API Plateform qui le fait donc ya pas trop de soucis.
Il n'y a pas de parité.
On peut pas prendre un document JSON que j'ai et puis le renvoyer en PUT pour le mettre à jour. Ça marchera pas.
En fait en REST, j'ai des solutions à ses problèmes aussi.
Je rajoute des groupes de serialization (comme on a vu dans ma conf d'hier) sur mon identité, et automatiquement je me retrouve avec l'auteur qui est directement imbriqué.
J'ai réglé le problème de l'over-fetching. Petite différence quand même, c'est le serveur qui dicte le format.
Le client, n'est pas capable de dire, cette fois ci j'ai besoin d'embarquer l'auteur.
Pour cette autre requête, je n'ai pas besoin. C'est quand même un peu moins bien que GraphQL.
On a une autre solution, qu'on appelle les Sparse Fieldsets. Cette fois ci, je mets une petite annotation : "@ApiFilter(PropertyFilter::class)", et là pareil, ça utilise une fonctionnalité (qu'on a vu lors de mon talk d'hier), qui s'appelle : l'attribute context dans le serializer Symfony, je peux juste, passer en paramètre de mon url, quels sont les propriétés que je souhaite y compris sur les relations, C'est pas le même langage, c'est moins élégant. La requête GET classique que j'envoie dans l'url.
C'est beaucoup moins élégant que GraphQL, mais ça fait la même chose et ça règle le problème.
Et il y a un autre avantage, c'est que, bon c'est moins élégant, on est complètement d'accord.
Mais en PHP, en Javascript, dans tous les langages de programmation, ça c'est dans le protocole HTTP et c'est implémenté depuis des années et ça marche.
Il n'y a pas besoin de parser spécifique.
Les filtres.
Je rajoute un filtre, par exemple pour faire la recherche sur une entité. Ça se fait comme ça avec APIPlatform. C'est s'implémenter de base si vous utilisez Doctrine.
Et je peux passer mon paramètres en REST. Mon filtrage est effectué.
Mes filtres sont documentés dans la documentation Hydra qui est fournie par APIPlatform. Donc mon client intelligent va pouvoir l'utiliser En GraphQL, pas de surprise, avec la même annotation que j'ai rajouté, j'ai le support des filtres dans GraphQL et je les passent en paramètres ici. Ça fonctionne à peu près la même manière.
Trier.
Juste une annotation à rajouter. Je veux pouvoir trier par tous mes champs.
Et ensuite, les paramètres que je passe dans l'url, je veux trier par titre et la description. Hop, C'est classé dans le bon ordre.
Et en plus, les filtres d'ordre que je peux appliquer, sont documentés grâce Hydra.
En GraphQL...
En GraphQL, il y a le support de la même chose.
Vous voyez, il y a quand même beaucoup de similarités entre GraphQL et REST.
Pagination.
Ça va pas être plus compliqué.
Avec APiPlatform, pour des raisons de sécurité, c'est activé par défaut. Vous n'avez rien à faire.
Dès que vous avez plus de 30 éléments dans votre page, vous pouvez, ajouté un paramètre qui va vous récupérez le numéro de la page.
Vous pouvez bien sûr tout configurer le nombre d'éléments par page, etc Dans ma doc Hydra, encore une fois, on me donne toutes les méta-informations qui permettent de naviguer dans ma collection côté client, sans avoir à le connaître à l'avance.
En GraphQL, c'est dans la spec Relay que c'est implémenté. C'est un peu différent parce que c'est de la pagination par curseur.
C'est supporté nativement par API Platform aussi. Et ça marche par défaut, c'est activé par défaut.
Faire des requêtes en écriture.
Je fais une requête PUT classique et hop ça marche. Avec API Platform c'est supporté par défaut.
Pour GraphQL on a un truc un petit peu compliqué. On peut envoyer plusieurs mutations dans une même requête.
Donc c'est supporté de base, ça marche tout seul, mais le clientMutationId, c'est le client qui doit régler un ID quand il envoie la requête.
On peut envoyer plusieurs mutations dans une même requête. Ça va me permettre, côté serveur, quand je récupère la réponse de savoir à quelle requête ça correspond. Ça va me permettre de faire l'appairage. C'est le seul truc un petit peu spécifique à savoir.
Bon maintenant on passe sur les parties qui fâchent un petit peu sur les cinq dernières minutes.
Donc on a eu de GraphQL, avait à peu près les mêmes capacités que les formats avancées de REST, qui sont pas si populaires encore que ça.
GraphQL, l'intérêt c'est que ça a popularisé ces API auto-découvrable etc On a vu que c'était hyper élégant, c'était hyper bien pensé dès le départ.
C'est vrai c'est vraiment quelque chose qui est très pratique à manipuler, surtout quand on fait du React côté client, parce que tout l'outillage qui va bien côté React, parce que c'est Facebook qui le fait pour consommer les API GraphQL, donc ça va très très vite.
Maintenant les parties qui fâchent un petit peu...
Déjà les logs. Alors ça c'est mes logs REST. Je vois vraiment mes requêtes en POST, en PUT, etc c'est assez clair c'est du log classique.
Ça c'est mes logs GraphQL. Tout est en POST vers endpoint /graphql. J'ai aucun détail.
Je sais pas si c'est de la lecture, de l'écriture. Pour les adminSys c'est super chiant. Je suis obligé de tout gérer côté applicatif. C'est un premier défaut.
Ensuite le cache.
Dans API Plateform (je passe très rapidement là dessus) un mécanisme de cache assez avancée qui fonctionne avec les capacités hypermédia.
En gros tout les requêtes, quand vous activez Varnish (il y a une option de config à mettre) elles sont générées une fois par PHP et ensuite elles sont toujours servis par Varnish, en quelques millisecondes.
Ça va super vite. Et dès qu'on a une requête en écriture sur une ressource, on détecte quelles sont les réponses HTTP qui ont été générés par le serveur qui contiennent des références à cette ressource ou cette ressource, les collections, les listes, les relations, tout ça et on va tous purger.
Du coup les données sont tout le temps fraîche mais sont servis le moins possible par PHP, parce que PHP malheureusement c'est quand même plus lent qu'un Varnish et ça le sera toujours.
Problème : en GraphQL, tout est du POST et du coup, ça marche pas le cache HTTP avec avec GraphQL.
On va être obligé de faire du cache au niveau applicatif.
Ce qui signifie qu'avec REST vous pouvez mettre du cache applicatif.
Par exemple doctrine cache, doctrine data cache. Vous mettre du cache HTTP côté serveur du cache HTTP côté client et du cache applicatif (type localStorage, sessionStorage) côté client, le top.
En GraphQL, vous allez pouvoir faire du cache mais par contre vous perdez les capacités du protocole HTTP.
Ce sera forcément du cache applicatif côté client côté serveur.
Ça aussi c'est vite embêtant pour nos adminSys, notre scalabilité etc, quand on n'a pas les capacités d'ingénierie d'un Facebook ou d'un Github.
Ça peut vite aussi, poser des problèmes de perfs.
Parce que nos requêtes sont très dynamiques du coup il y a de très grandes chances, qu'elles tapent jamais nos index en base de données.
C'est le même problème avec les Sparse Fieldsets de REST. là-dessus il n'y a pas de secret.
Les grosses API GraphQL, généralement ça utilise ElasticSearch, du Neo4J pour régler ces index là.
Si vous utilisez PostgreSQL ou MySQL, que ce soit en REST, avec du Sparse Fieldset ou en GraphQL, faites super gaffe à ce que vous allez peut-être, créé des problèmes de performance, voire de sécurité en créant des vecteurs d'attaque DDOS En parlant de sécurité et fiabilité, pour REST c'est hyper carré on a des spécifications OWASP qui disent exactement ce qu'il faut faire.
Qui d'ailleurs sont implémentées de base par l'API Platform.
On a toute une documentation pour vérifier que notre API est sécurisé on a beaucoup de retours d'expérience là dessus.
Avec GraphQL, c'est beaucoup plus récent, beaucoup moins de retour.
Il ya déjà eu des failles assez costaud qui ont été exploitées là dedans.
Et vu qu'on peut laisser le client exécuter n'importe quel type de requêtes aussi complexe qui soit, c'est un gros vecteur d'attaqué par DDOS.
Il ya des mécanismes de protection.
Ils sont dispo dans la bibliothèque webonyx/graphql-php, type du timeout, type limiter la complexité de la requête mais c'est quand même quelque chose qui est moins connu et sur lequel il faudra faire plus attention d'un point de vue sécurité.
Et en termes de fiabilité, on peut faire des trucs bien, on peut toujours, mais pareil on est quand même sur quelque chose de beaucoup moins mature encore que REST.
Et même l'API Github, que j'ai pas mal utilisé (parce que je suis en train faire un projet là dessus et pour préparer ce talk), des fois pour des requêtes qui sont pas si compliquées, vous voyez c'est celle de tout à l'heure que je vous ai montré.
He ben ça plante. Alors que leurs API REST, elle plantait, mais quand même beaucoup plus rarement que celle là.
Donc c'est quand même quelque chose sur lequel il va vous falloir des équipes costaud et des développeurs qui savent ce qu'ils font qui n'ont pas peur d'aller trifouiller dans les couches bas niveau.
Gros avantage de GraphQL : pas de versionning.
On peut juste déprécier des types et des champs.
En fait, en REST on peut faire exactement la même chose. Ça s'appelle API evolution et teaser...
...ça va être dispo dans la prochaine version d'API Platform pour REST et pour GraphQL, mais c'est encore le cas.
Côté client, il commence à y avoir l'outillage pour, entre autres ceux d'API Platform, pour tout ce qui est à Hydra et JSON-LD.
Par contre là, gros avantage à GraphQL, l'écosystème Facebook dont j'ai parlé, permet de faire des choses assez hallucinante côté client et s'est pas encore au même niveau côté REST.
Donc là, si vous utilisez le full stack Facebook, c'est quand même pas mal du tout côté GraphQL, même si ça évolue côté REST.
Finalement, petit écho à la conf d'hier, faites gaffe avec GraphQL, s'est fait en silo c'est fait par Facebook pour Facebook c'est fait pour un web extrêmement centralisé, où toutes les données sont servis par un seul prestataire. C'est vraiment le cas d' usage Facebook de base.
À l'inverse, le W3C est assez vigilants à ce que les spécifications ouvertes sur REST soit fait pour un web décentralisé, pour être capable d'agréger des données de différentes sources pour que ce soit conçu dans le protocole de base Du coup, les techno Facebook et Facebook en particulier, ça a quand même créé pas mal de petits soucis.
"Cambridge Analytica" et tout ça.
Ce qu'on fait les développeurs en permettant cette concentration des données a des conséquences éthiques assez importantes, qui sont aussi liées aux formats et à la manière dont on l'utilise. Donc là aussi quand vous faites votre API, faites le pas les yeux fermés et réfléchissez un peu plus loin que la simple technique. Effectivement Relay, ça marche super bien c'est hyper facile peut-être que ça pose (suivants le domaine dans lequel vous travaillez), ça pose des problèmes un petit peu plus importants d'un point de vue éthique.
Pour résumer.
GraphQL c'est super bien, c'est super élégant, c'est vraiment bien foutu, l'écosystème est complet et ça marche de base en faisant, pas grand chose dans API Platform.
Si vous le faites vous même, c'est pas si simple à implémenter et les fonctionnalités les plus intéressantes, c'est peut-être pas en PHP qu'il faut les faire.
Peut-être plutôt aller du côté de Node.js ou de Go, parce qu'il faut du temps réel.
REST, ça permet de faire finalement quasiment la même chose de manière un petit peu plus...
...ancienne, un petit peu moins élégante mais beaucoup plus mature avec beaucoup plus de retours d'expérience.
Vous pouvez aussi utiliser les deux une API GraphQL, pour faire des requêtes complexes pour les projets internes pour votre appli mobile sur lequel vous avez le contrôle, etc.
Une API publique en REST bien mature avec des capacités finalement assez similaire, et avec API Platform avec le même code vous allez pouvoir exposer les deux formes.
Merci beaucoup.
Commentaires