Comment GraphQL pourrait nous aider à améliorer l’espace client OVH

Temps de lecture estimé : 10 minute(s)

*Attention, ce contenu a été publié il y a 2 années. Il n'est peut-être plus d'actualité.*

Les premiers pas des développeurs UX OVH avec GraphQL, la spécification initiée par Facebook pour construire des back-ends adaptés aux applications front-end actuelles.

 

GraphQL chez OVH par la team UX

Chez OVH, l’équipe développeurs UX est chargée de développer l’ensemble des interfaces client, depuis le tunnel de commande sur le site ovh.com jusqu’à l’espace client (front-end et back-end associé).
Notre équipe est le premier client des API fournies par OVH, que nous consommons pour accéder aux informations au même titre que nos clients. Autrement dit, toutes les informations que nous affichons au sein de l’espace client OVH sont aussi disponibles à travers l’API publique OVH. Cette API est développée selon la spécification REST qui est rapidement devenue un standard pour bon nombre d’API publiques.
Si vous avez déjà eu l’occasion de développer un outil front-end, dans ce cas un espace client avec beaucoup de données à récupérer, vous le savez : une API REST peut rapidement poser des problèmes de performance. L’architecture REST est conçue pour isoler chaque ressource dans un endpoint différent. Dès lors que l’on doit afficher plusieurs ressources, le nombre de requêtes augmente drastiquement. Or les navigateurs limitent le nombre de requêtes à effectuer en parallèle. Et, une fois les limites atteintes, les requêtes excédentaires sont mises en attente par le navigateur.

Inspection des requêtes pour afficher la page informations d’un hébergement web

Vous l’aurez compris, chez OVH pour pouvoir afficher les données du manager dans un temps relativement court, nous avons mis en place ce qu’on appelle un back-end for front-end (BFF). Il nous permet de limiter le nombre de requêtes effectuées en front et évite ainsi que ne se constitue une longue file d’attente de requêtes HTTP. Actuellement ce BFF est une API en microservices Node.js grâce à laquelle nous réalisons de l’agrégation de données (via api.ovh.com) et dans certains cas du calcul, pour déporter la charge de travail côté serveur. Ce back-end résout en partie notre souci de saturation côté navigateur mais il présente cependant quelques inconvénients.

Schéma exemple d’un back-end for front-end
Schéma exemple d’un back-end for front-end

Par exemple, pour afficher certaines pages ou certaines sections de l’espace client, nous réutilisons le même microservice afin de ne pas les multiplier inutilement. De ce fait, nous allons parfois requêter des informations sur l’API dont nous n’avons pas forcément besoin, ce qui induit un temps de réponse plus long. Autre exemple : dans certaines situations, nous devons redéployer le microservice et le front pour une modification. L’équipe UX d’OVH étant très agile, la possibilité de redéployer uniquement le front entraînera un gain de temps afin de mettre en production notre application plus rapidement.

Introduction à GraphQL et exemples d’utilisation

Suite à un Meetup auquel j’ai assisté traitant de GraphQL, j‘ai rapidement trouvé des cas d’utilisation de cette spécification au sein de l’équipe UX. Pour ceux qui ignorent tout de GraphQL, laissez-moi vous présenter brièvement cette spécification et vous fournir quelques liens utiles. Après cette présentation succincte, vous comprendrez rapidement pourquoi nous étudions la possibilité d’utiliser cette spécification pour le back-end de notre espace client.

 

Logo GraphQL

GraphQL est une spécification concernant un langage de requête qui a été initié par les ingénieurs de Facebook en 2012. L’idée n’est donc pas tout à fait nouvelle, mais les choses se sont accélérées depuis que Facebook a décrit cette spécification afin d’en faire un standard. Attention à ne pas confondre : GraphQL n’est pas un langage de requête pour bases de données, mais une spécification pour ce langage. De plus, GraphQL est souvent utilisé pour une API en HTTP, mais ce n’est pas le seul cas d’utilisation possible : le standard est agnostique de la couche de transport utilisée.

Avec GraphQL il faut complètement mettre de côté les spécificités de REST. Une première grande différence réside dans le fait qu’il n’y a plus qu’un seul endpoint qui va nous permettre d’accéder aux différentes ressources. Cet endpoint est un POST qui va prendre dans son body la requête GraphQL. Pour faire simple, voici un exemple de requête en GraphQL :

 

Ce n’est pas un JSON valide c’est un format propre à GraphQL. Dans cette requête, nous demandons d’avoir la ressource “posts” avec comme détails son “title” et la ressource “author”.

 

 

Exemple d’erreur quand on demande le champ “age” sur “author” qui n’existe pas.

Avec cette simple requête, on peut déjà s’apercevoir de différents avantages : le résultat sera sous le même format (JSON cette fois-ci) avec les données, plus du tout de surprises concernant le retour de l’API. L’avantage est aussi que l’implémentation GraphQL s’occupe de la validation des données. Si on requête une ressource qu’il ne connait pas, il va directement renvoyer l’erreur appropriée, puisqu’en réalité dans le code nous lui déclarons un schéma de données ainsi que le type associé.

GraphQL gère notamment les cas d’erreurs partielles. Imaginons qu’une ressource est indisponible pour le moment mais que tout le reste fonctionne correctement. Dans cette situation GraphQL va nous renvoyer tout ce qu’il sait récupérer et indiquer les champs qu’il n’a pas pu récupérer. Dans notre application front-end nous pourrons afficher certaines informations, plutôt que de ne rien afficher du tout.

En contrepartie, GraphQL requête en parallèle tout ce qu’il peut. Par conséquent, si vous utilisez GraphQL dans un langage qui ne gère pas l’asynchrone, les performances risquent d’être catastrophiques. Un des principaux atouts de cette spécification est aussi le fait d’aller chercher uniquement la ressource demandée. De cette façon si “authors” n’est pas renseigné dans la query GraphQL, alors celle-ci n’ira pas chercher l’information et nous profiterons d’un temps de réponse plus court.

Autre cas pratique : le filtrage des données. Par exemple, si je veux filtrer et n’afficher que les publications dont l’auteur est “OVH”, voici la requête :

{
  posts {
    title,
    author(name: "OVH") {
      name
      twitterHandle
    }
  }
}

Pour un endpoint de ce type, on peut disposer d’une documentation basique assez rapidement grâce à l’introspection. En effet, puisque l’on déclare les schémas et les types que l’on peut requêter, il existe un outil qui s’appelle “GraphiQL”, qui permet d’avoir de l’auto-complétion de la requête GraphQL dans le navigateur.

Implémentation GraphQL en Node.Js

Il existe plusieurs implémentations dans différents langages, celle qui semble être la plus maintenue et documentée est graphql-js en Node.js. C’est celle-ci que nous avons utilisée pour réaliser un POC et c’est avec cette librairie qu’ont été construits les exemples qui vont suivre.
Pour bénéficier de la validation ainsi que de l’introspection GraphQL, nous avons besoin de déclarer notre schéma avec ses différents types. Voici à quoi cela ressemble :

const { GraphQLObjectType, GraphQLString, GraphQLSchema, GraphQLList, GraphQLID, GraphQLInt, GraphQLFloat, GraphQLBoolean } = require("graphql");

const ssl = new GraphQLObjectType({
  name: "Ssl",
  fields: {
    provider: {
      type: GraphQLString
    },
    status: {
      type: GraphQLString
    },
    regenerable: {
      type: GraphQLBoolean
    },
    type: {
      type: GraphQLString
    },
    domains: {
      type: new GraphQLList(GraphQLString),
      description: "Domains linked to the certificate",
      args: {
        length: { type: GraphQLInt }
      },
      resolve(parentValue, args, request) {
        return request.ovhRequest.getAsync(`/hosting/web/${parentValue.serviceName}/ssl/domains`)
          .then((domains) => {
            if (args.length) {
              return domains.slice(0, args.length);
            }

            return domains;
          });
      }
    }
  }
});
Exemple de schéma GraphQL

Dans l’exemple ci-dessus, nous avons plusieurs parties très intéressantes à analyser. Les types comme par exemple “GraphQLString” viennent de la librairie graphql-js disponible sur NPM. Si vous souhaitez plus d’informations concernant ces types, n’hésitez pas à consulter la documentation. Nous avons aussi utilisé la librairie NPM express-graphql qui nous permet de faire le lien entre expressjs et GraphQL.

Dans le champ “domains”, il y a la partie “args” qui représente le paramètre à partir duquel on voudrait filtrer, dans notre exemple, je lui indique un paramètre nommé “length”. Donc pour l’utiliser dans ma requête, j’écris simplement “domains(length: 2)” ce qui limitera les domaines à afficher à 2 maximum.

Ce paramètre, nous le récupérons justement dans la fonction “resolve”, qui comme son nom l’indique résout une promise ou une valeur pour trouver la donnée. Le paramètre “parentValue” représente les valeurs de l’élément parent, car le type “SSL” est un sous-type de Hosting, donc “parentValue” aura les valeurs de “hosting”. Et dans “request” nous retrouvons le contexte de la requête effectuée.

const hosting = new GraphQLObjectType({
  name: "Hosting",
  fields: {
    serviceName: {
      type: GraphQLID
    },
    offer: {
      type: GraphQLString
    },
    hasHostedSsl: {
      type: GraphQLBoolean
    },
    ssl: {
      type: ssl,
      resolve(parentValue, args, request) {
        return request.ovhRequest.getAsync(`/hosting/web/${parentValue.serviceName}/ssl`)
          .then((ssl) => Object.assign({}, ssl, { serviceName: parentValue.serviceName }));
      }
    },
    serviceInfos: {
      type: serviceInfos,
      resolve(parentValue, args, request) {
        return request.ovhRequest.getAsync(`/hosting/web/${parentValue.serviceName}/serviceInfos`);
      }
    }
  }
});
Type Hosting en GraphQL

Vous l’aurez compris : pour chaque donnée, vous pouvez spécifier un resolver pour aller la chercher où vous le souhaitez. De cette façon, cela fait une certaine abstraction et si dans le futur, notre donnée se trouve à un endroit différent ou si l’on recourt à une technologie différente, nous devrons juste changer le resolver lié à celle-ci. Cela constitue un atout indéniable lorsqu’on migre sur de nouvelles infrastructures par exemple.

Conclusion

GraphQL permet donc de résoudre les problèmes évoqués au début d’article, entre autre liés à l’utilisation d’une API Rest en front. De plus, grâce à cette spécification, nous pouvons facilement migrer vers une architecture différente avec un effort raisonnable. Si vous souhaitez en apprendre d’avantage sur ce sujet, n’hésitez pas à visiter les liens situés en bas de ce post. J’en profite également pour vous signaler que nous avons créé un Gitter afin de discuter de sujets techniques. Venez y discuter avec nous et partager votre expérience si vous avez déjà utilisé GraphQL !

Sources

Développeur au sein de la team UX chez OVH.