Comprendre GraphQL en 5 minutes

En dehors du titre, le générique masculin est utilisé sans aucune discrimination et uniquement dans le but d'alléger le texte.

Comprendre GraphQL en 5 minutes

Quand GraphQL est arrivé sur les Internets, il s’est propagé comme une épidémie. GraphQL a complètement changé la façon de faire les choses et continue sa progression partout. Si t’as cinq minutes devant toi, je t’explique tout ce que tu dois savoir.



Made in Facebook

En 2012, l’adoption des téléphones mobiles atteint des chiffres monstrueux dans le monde entier. C’est tellement l’invasion que les entreprises qui n’adaptent pas leurs produits sont en danger. À ce moment-là, c’est le cas de Facebook.

Facebook fait du web. Du coup, ils ont fait leur app IOS comme un site web, en web-view. Très vite, ils se rendent compte que c’est un peu de la merde (à cette époque). Ils décident alors de la refaire entièrement en natif, pour une meilleure expérience client. Immédiatement ils se prennent un autre mur dans la face.

L’architecture existante ne fonctionne pas. Principalement parce que les endpoints de leurs api REST existantes ne permettent pas de flexibilité sur la data. Plusieurs aller-retour sur différents endpoints sont nécessaires pour de la donnée imbriquée, causant des lenteurs et des inconsistances. Une partie du payload n’est pas nécessaire pour la plupart des requêtes, causant des transferts de données inutiles. Et surtout c’est fastidieux pour Facebook de gérer autant de call HTTP.

C’est dans ce contexte infernal qu’en février 2012 Lee Byron, Dan Schafer et Nick Schrock se réservent des bureaux dans un coin de Facebook.





Très vite un premier prototype de GraphQL, appelé alors SuperGraph, est produit par nos trois devs. En août 2012, GraphQL est shippé en prod avec la nouvelle app native de Facebook. En 2015, la première version public arrive sur les internet. GraphQL est toujours présent aujourd’hui quand tu scroll ton mur Facebook. Mais comment ont-il réglé un problème qui touchait non seulement Facebook, mais aussi toute l’industrie ?



C’est quoi GraphQL ?

GraphQL est un langage de requêtes de données pour API. QL, comme dans SQL, veut dire Query Language. GraphQL permet de manipuler de la donnée de façon simple, flexible et très précise. GraphQL n’est pas un langage de programmation ou un framework. GraphQL est une spécification pour implémenter ton API. Concrètement, dans l’utilisation, ça ressemble à ça.

Requête

{
    pokemons {
        name,
        abilities {
          name,
          damage,
          accuracy,
          mana,
          type
        }
    }
}

Réponse

{
    "data": {
        "pokemons": [
            {
                "name": "pikachu",
                "abilities": [
                    {
                        "name": "Thunder punch",
                        "damage": 75,
                        "accuracy": 70,
                        "mana": 15,
                        "type": "physical"
                    },
                    {
                        "name": "Thunderbolt",
                        "damage": 90,
                        "accuracy": 80,
                        "mana": 15,
                        "type": "electric"
                    }
                ]
            },
            {
                "name": "mewtwo",
                "abilities": [
                     {
                        "name": "Earthquake",
                        "damage": 130,
                        "accuracy": 100,
                        "mana": 20,
                        "type": "ground"
                    },
                    {
                        "name": "Brutal swing",
                        "damage": 180,
                        "accuracy": 90,
                        "mana": 25,
                        "type": "physical"
                    }
                ]
            }
        ]
    }
}


C’est comme ça qu’on demande et qu’on reçoit de la donnée en utilisant GraphQL. OK, pour le moment, c’est pas clair. Déjà, ca rentre ou dans ton architecture ce machin ?





Le bonhomme qui sourit, c’est toi. Et toi, pour faire le payload que je t’ai montré plus haut avec les Pokemon et leur habilité tu galères. Tu galères parce que l’API REST que tu utilises n’est pas fait pour ton besoin. Tu te retrouves à faire un call par Pokemon, puis un call par habilité pour chaque Pokemon.

À chaque fois la logique de ton application fait une demande à la base de données et te renvoie un bout de payload. Et du coup, malgré ton sourire apparent, t’as envie de te tirer une balle. C’est la que GraphQL intervient.





Avec GraphQL, finie la galère. Tu fais un seul POST et tu demandes exactement ce que tu veux via une requête GraphQL. Ensuite, c’est le serveur qui gère sa merde, toi tu reçois ton payload complet.

Avec REST tu recevais des objets définis par des endpoints. Avec GraphQL, tu ne t’adaptes pas un objet défini par le backend, tu définis dynamiquement l’objet que tu vas recevoir côté client. Et ça, ça change tout.

OK alors, c’est bien beau tout ça, mais concrètement comment ça marche ? Comment GraphQL accède à ta base de données et fait des requêtes ? Pour vraiment comprendre GraphQL, il faut mettre les mains dedans.



Fais voir le code

Je vais te faire une implémentation côté Javascript (NodeJS). Mais sache que tout ce qui suit est applicable dans n’importe quels langages. La logique GraphQL reste la même partout puisqu’il s’agit avant tout d’une spécification.

Pour commencer à bosser sur du GraphQL direction le site officiel et leur liste d’implémentation dans tous les langages de la terre. Pour faire simple avec NodeJS on a besoin des modules express-graphql et graphql. Commençons par monter le serveur de base.



index.js

const path = require("path");
const express = require("express");
const graphqlHTTP = require("express-graphql");
const graphql = require("graphql");

const { query } = require(path.resolve("schema/query"));
const graphQLSchema = new graphql.GraphQLSchema({ query });

const app = express();

app.use(
  "/graphql",
  graphqlHTTP({
    schema: graphQLSchema,
    graphiql: true
  })
);

app.listen(8080);


Tout d’abord on appelle nos dépendances. Ensuite ligne 6 on va chercher notre requête racine qu’on passe dans le schéma principale ligne 7. On lance notre serveur express, on expose la route /graphql via un middleware express et enfin on écoute sur le port 8080. Voyons ce qui se passe à l’intérieur du schéma maintenant.



schema/query.js

const path = require("path");
const { GraphQLObjectType, GraphQLList } = require("graphql");
const { pokemonsType } = require(path.resolve("schema/types"));

const RootQuery = new GraphQLObjectType({
  name: "RootQueryType",
  type: "Query",
  fields: {
    pokemons: {
      type: new GraphQLList(pokemonsType),
      resolve() {
        const data = require(path.resolve("data/pokemons.json"));

        return data;
      }
    }
  }
});

exports.query = RootQuery;

Le schéma est central dans GraphQL. Il va dicter la communication entre ton client et ton serveur. Il spécifie les requêtes que tes clients peuvent faire, les types de données récupérables et les relations entre ces types. Tout est défini dans ce schéma. À commencer par la requête racine.

La requête racine permet à GraphQL de savoir quel type de données est possible d’aller chercher. Et là, dans ma requête racine, je spécifie que j’ai un champ pokemons ligne 9 de type liste de type pokemon ligne 10.

Ensuite on a un résolveur ligne 11. C’est les résolveurs qui font le travail d’aller chercher ta data dans ta base de données. Un résolveur est assigné à chacun de tes champs. Et le résolveur de mon champ pokemons est une liste d’objet pokemon. Mon résolveur ici renvoie la data via un fichier JSON qui correspond à un array d’objet pokemon.

Je renvoie un JSON pour la data par souci de simplicité et concision. Mais dans la vraie vie c’est ici que t’es censé appeler ta base de données, faire des requêtes et renvoyer la data. Maintenant, voyons à quoi ressemble les types.



schema/types.js

const path = require("path");
const graphql = require("graphql");
const { GraphQLObjectType, GraphQLString, GraphQLList } = graphql;

const abilitiesType = new GraphQLObjectType({
  name: "ability",
  fields: {
    name: {
      type: GraphQLString,
      resolve: parent => parent.name
    },
    damage: {
      type: GraphQLString,
      resolve: parent => parent.damage
    },
    accuracy: {
      type: GraphQLString,
      resolve: parent => parent.accuracy
    },
    mana: {
      type: GraphQLString,
      resolve: parent => parent.mana
    },
    type: {
      type: GraphQLString,
      resolve: parent => parent.type
    }
  }
});

const pokemonsType = new GraphQLObjectType({
  name: "pokemons",
  fields: {
    name: {
      type: GraphQLString,
      resolve: parent => parent.name
    },
    abilities: {
      type: new GraphQLList(abilitiesType),
      resolve(parent) {
        const abilities = require(path.resolve("data/abilities.json"));

        return abilities.filter(ability =>
          ability.linkedTo.includes(parent.name)
        );
      }
    }
  }
});

exports.pokemonsType = pokemonsType;

Le principe reste le même. On crée des types d’objets GraphQL qui représentent notre structure de données. On spécifie des champs et pour chaque champ, on assigne un résolveur qui va chercher la bonne data. Il est intéressant de voir ici que j’utilise le contexte du parent pour filtrer quelles habilitées renvoyer pour chaque pokémon ligne 44.

Si tu veux voir une version fonctionnelle de cette implémentation, je t’ai fait une petite sandbox publique où tu peux jouer avec. Tu peux voir tous les fichiers, dont les fichiers JSON, et modifier ce que tu veux ! Aller, je fais le fifou, je te l’embed même juste en dessous.





Toi aussi tu pourrais faire le fifou. À la place de la donnée via fichier JSON tu pourrais implémenter la même chose en faisant des fech sur PokéAPI. Ça te permettrait de pratiquer GraphQL en plus.



Épilogue

Voilà, je ne peux pas aller plus loin dans la présentation. Je dépasse déjà les cinq minutes du temps que tu m’as accordé. Y’a beaucoup plus à dire sur cette techno. Les mutations, le cache, les variables et les contextes. Je vais me contenter de l’essentiel. Si tu veux en savoir plus et que tu as du temps devant toi, je te conseille cet article très complet !

Qui me parle ?

jesuisundev
Je suis un dev. En ce moment je suis Backend Développeur / DevOps à Montréal. Le dev est l'une de mes passions et j'écris comme je parle. Je continue à te parler quotidiennement sur mon Twitter. Tu peux m'insulter à cet e-mail ou le faire directement dans les commentaires juste en dessous. Y'a même une newsletter !

9 commentaires sur “Comprendre GraphQL en 5 minutes”

  1. Les inconvénients que tu cite pour une API REST tiennent pour une API type CRUD développée comme un cochon. Une API suivant la json-api par exemple est beaucoup plus flexible avec des possibilités de tri, filtrage, inclusion d’objet dépendant etc… . Après cela n’enlève rien a graphql.

    1. Exact ! C’est pour ça que je dis que l’API n’est pas fait pour le besoin.
      C’est plus pour faire comprendre plus rapidement le concept de GraphQL qu’autre chose.

  2. L’architecture de node est particulièrement compliquée pour faire des json-api. En tout cas c’est mon avis. Toujours établir les relations dans les requêtes Sequelize (ce qu’on utilisait au travail pour ça), c’était vraiment fastidieux. C’est aussi pour cette raison que j’ai migré sur Laravel pour ça. Tout se fait simplement avec des ressources qu’on renvoie, il va chercher tout ça tout seul comme un grand avec le modèle.
    Mais GraphQL est juste génial, j’ai eu l’occasion de jouer avec sur un side project. ❤️
    Merci pour tout article 🙂

  3. Du coup cela ne remplace quand même pas la REST API car ces n’est que de la lecture, non?
    Il faut quoi qu’il arrive coder ce qu’il faut pour insérer les objets.

T'en penses quoi ?

Your email address will not be published. Required fields are marked *