Skip to content

js-republic/graphql-workshop

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GraphQL Workshop for Best Of Web

Ce workshop préparé au sein de JS-Republic par Michael Romain et Mathieu Breton a pour but de vous apprendre à utiliser GraphQL.

Introduction

D'une durée approximative de 3h, ce workshop vous guidera étape par étape dans la migration d'un blog basique fait avec une API REST vers une implémentation full GraphQL. Chaque partie débutera par une courte présentation faite par l'animateur du workshop afin d'expliquer les points qui seront traités dans celle-ci, puis les candidats suivront l'énoncé pour accomplir la partie en question. Une fois l'étape terminée, l'animateur répondra aux questions et passera à la partie suivante.

Chaque étape vous démontrera les avantages (et inconvénients) de GraphQL comparé à REST pour les mêmes besoins. Ce workshop est conçu pour des développeurs JavaScript, débutants en GraphQL.

Pour commencer, assurez-vous d'avoir les pré-requis ci-dessous puis procéder à l'installation du projet de workshop. Une description du projet vous attend une fois l'installation terminée.

Pré-requis

Pour suivre ce workshop, vous aurez besoin :

  • De connaissances confirmées dans le langage JavaScript, en NodeJS et en développement Front-End.
  • D'une prémière expérience avec les API REST.
  • De NodeJS installé en version 6.14.2 et plus. Dans un soucis de compatibilité, l'implémentation back-end fonctionne avec la version 6.* de Node, version la plus vielle actuellement encore maintenue. Si vous utilisez nvm, vous pouvez faire un nvm use à la racine du projet pour passer directement dans la bonne version de NodeJS.
  • D'un éditeur de code. Visual Studio Code fait désormais référence.

Installation

Une fois n'est pas coutume, nous récupérons ce projet depuis Github et installerons ses dépendances :

git clone https://github.com/js-republic/graphql-workshop.git
cd graphql-workshop
npm install

Il ne reste plus qu'à le démarrer :

npm start

Votre navigateur s'ouvre à l'adresse http://localhost:3000, vous devriez découvrir cette interface :

alt Interface du blog

Prenez quelques instants pour vous familiariser avec le blog en l'état. Vous remarquerez notamment que, pour l'instant, il communique avec le back-end via l'API REST.

Description du projet du workshop :

Le projet est organisé comme suit :

.
├── ...
├── blog.sqlite                   <-- Fichier de base de données SQlite du blog
├── migrations                    <-- Dossier contenant les scripts d'initialisation SQL
├── public                        <-- Dossier public exposé sur localhost:3000
│   ├── index.html
│   └── ...
├── server                        <-- Sources du serveur en NodeJS exposant les données
│   ├── ...
│   ├── route                     <-- Dossier contenant les routes exposées par le serveur
│   │   ├── graphql.js            <-- Script pour exposer les données en GraphQL (à modifier)
│   │   ├── rest.js               <-- Script pour exposer les données en REST
│   │   └── ...
│   └── service
│       └── index.js              <-- Service qui permet d'accéder et modifier les données en base
└── src                           <-- Sources du front en React (architecture create-react-app)
    ├── ...
    ├── clients                   <-- Dossier contenant les routes exposées par le serveur
    │   ├── rest.js               <-- Script permettant la récupération et manipulation des données via REST
    │   └── graphq.js             <-- Script permettant la récupération et manipulation des données via GraphQL (à modifier)
    └── components
        └── ...

Quand le projet est démarré (via npm start) deux tâches sont lancées en parallèle :

Si vous faites ce workshop hors de la session Best Of Web, nous vous invitons à d'abord prendre connaissance du début de cette présentation jusqu'à la diapositive Premier exercice : https://slides.com/mbreton/graphql-workshop

Les données sont enregistrées dans SQLite, sous la forme d'un fichier blog.sqlite à la racine du projet. Si vous souhaitez réinitialiser vos données, il vous suffit de supprimer ce fichier et redémarrer.

Exercices

Familiarisation avec GraphQL

Dans cette première partie, vous allez vous familiariser avec le requêtage GraphQL et l'implémentation côté serveur pour lire des données.

Présentation des points abordés : https://slides.com/mbreton/graphql-workshop#/1

Énoncé :

  1. Rendez-vous dans le ficher server/route/graphql.js pour y ajouter un type Query posts permettant de récupérer une liste de Post.

    Découvrir la solution ici
    // ...
    const typeDefs = `
    type Post {
      id: ID!
      title: String!
      content: String!
    }
    type Query {
      posts: [Post]
    }
    `);
    // ...
    
  2. Ajouter un resolver correspondant à la Query que venez d'ajouter. Vous pouvez vous appuyer sur la fonction getPosts du script server/service/index.js déjà importé dans server/route/graphql.js. Petite subtilité, cette fonction retourne une promesse. (plus d'information sur les resolvers asynchrones ici)

    Découvrir la solution ici
    // ...
    const typeDefs = `
    type Post {
      id: ID!
      title: String!
      content: String!
    }
    type Query {
      posts: [Post]
    }
    `);
    router.use(
        graphqlHTTP({
            schema,
            rootValue: {
                posts() {
                    return service.getPosts();
                }
            },
            graphiql: true
        })
    );
    // ...
    

Vous devez maintenant pouvoir requêter la liste des articles via GraphQL dans GraphiQL.

Comment requêter les données ?
    {
        posts {
            id
            title
            content
        }
    }
    

Graphql, partie client

Nos données désormais disponibles via GraphQL sur notre serveur, il est temps de les afficher !

Présentation des points abordés : https://slides.com/mbreton/graphql-workshop#/2

Énoncé :

  1. Aller dans le fichier src/clients/graphql.js. Ce fichier est responsable de toutes les requêtes GraphQL envoyées par le front vers le backend. Implémenter la fonction getPosts, pour l'instant vide, afin de charger l'id, le title et le content des articles :

    export async function getPosts() {}

    Vous pouvez utiliser la librairie axios pour faire vos appels HTTP. Vérifiez que vous avez bien implémenté la fonction en retournant sur le blog http://localhost:3000 voir que la liste des articles se charge bien avec l'interrupteur en mode GraphQL, sinon regardez la console :)

    Découvrir la solution ici
    export async function getPosts() {
        const {
            data: {
                data: { posts }
            }
        } = await axios.post("/graphql", {
            query: `{
            posts {
                id
                title
                content
            }
            }`
        });
        return posts;
    }
    

Retour sur le typage

Vous ne l'aurez pas manqué, il n'y a plus les commentaires ! Et cela est normal, ils n'existent pas dans notre schéma.

Nous confirmons des points déjà vus juste avant dans cette partie.

Énoncé :

  1. Retournez dans server/route/graphql.js pour y ajouter un type Comment dans le schéma. Ce type Comment contiendra une propriété id qui représente son identifiant unique et une propriété content qui représente le texte qu'elle contient. Ces deux propriétés doivent être obligatoires. Une fois ce type créé, ajouter une propriété comments dans Post qui contiendra la liste du type fraichement créé.

    Découvrir la solution ici
    // ...
    const typeDefs = `
    type Post {
      id: ID!
      title: String!
      content: String!
      comments: [Comment]!
    }
    type Comment {
      id: ID!
      content: String!
    }
    type Query {
      posts: [Post]
    }
    `);
    // ...
    
  2. Dans le même fichier, ajouter un resolver pour la propriété comments du type Post. Se revolver devra charger les commentaires à l'aide de la fonction getCommentFor du service en lui passant en paramètre l'id du post parent.

    Découvrir la solution ici
        const typeDefs = `
            type Post {
                id: ID!
                title: String!
                content: String!
                comments: [Comment]!
            }
            type Comment {
                id: ID!
                content: String!
            }
            type Query {
                posts: [Post]!
            }
            `;
            const resolvers = {
                Query: {
                    posts() {
                    return service.getPosts();
                    }
                },
                Post: {
                    comments(post) {
                    return service.getCommentsFor(post.id);
                    }
                }
            };
    
  3. Il ne reste plus qu'à modifier la requête de la fonction getPosts du fichier src/client/Graphql.js pour y ajouter le chargement des commentaires (et de toute le ses propriétés) en même temps que celui des Posts.

    Découvrir la solution ici
        export async function getPosts() {
            const { data: { data: { posts } } } = await axios.post("/graphql", {
                query: `{ posts { id title content comments { id content } } }`
            });
            return posts;
        }
    

Comparez maintenant les appels réseaux du blog en mode REST et en mode GraphQL. Qu'observe-t'on ?

Nous venons de mettre le doigt sur une des grandes forces de GraphQL : le requêtage multiple

Là où REST impose que chaque ressource doit être derrière une URL, GraphQL permet de récupérer plusieurs entitées, liées ou non, en une seule requête.

Création d'un commentaire via GraphQL

Nous savons désormais exposer et lire de la donnée à travers GraphQL, voyons maintenant comment nous pouvons la modifier par ce biais. Pour l'instant, quand vous êtes en mode GraphQL, l'ajout d'article ne fonctionne pas.

Présentation des points abordés : https://slides.com/mbreton/graphql-workshop#/3

Énoncé :

  1. Implémentons d'abord la partie serveur. Comme lors du premier exercice, rendez-vous dans server/route/graphql.js. Ajouter dans le schéma un nouveau type Mutation dans lequel il y aura une opération createComment prenant en paramètre un postId, de type ID, content de type string et retournant le type Comment.

    Découvrir la solution ici
    const typeDefs = `
    type Post {
        id: ID!
        title: String!
        content: String!
        comments: [Comment]!
    }
    type Comment {
        id: ID!
        content: String!
    }
    type Query {
        posts: [Post]!
    }
    type Mutation {
        createComment(postId:ID!, content: String!): Comment
    }
    `);
    
  2. Complétons notre implémentation serveur, en ajoutant le resolver correspondant à la mutation précédement ajoutée. Cette opération pourra s'appuyer sur la fonction addNewCommentFor du service server/service/index.js.

    Découvrir la solution ici
    const resolvers = {
        Query: {
            posts() {
                return service.getPosts();
            }
        },
        Mutation: {
            createComment(parentValue, args) {
                return service.addNewCommentFor(args.postId, args.content);
            }
        },
        Post: {
            comments(post) {
                return service.getCommentsFor(post.id);
            }
        }
    };
    
  3. Retourner dans GraphiQL, et formuler une requête graphql pour tester la mutation mise en place côté serveur. Vous devriez recevoir un résultat du type (en fonction des champs que vous choisissez de récupérer):

    {
      "data": {
        "createComment": {
          "id": "f1fbde00-696c-11e8-b3cf-bf211aef93e8",
          "content": "contentdzdzdz"
        }
      }
    }
    Découvrir la solution ici
    mutation addNewComment {
      createComment(
        postId: "f1fbde00-696c-11e8-b3cf-bf211aef93e8",
        content: "contentdzdzdz"
      ) { id content }
    }
    
  4. La partie serveur maintenant prête, il est temps d'ajouter le code côté front capable d'envoyer une requête de mutation au serveur. Vous l'aurez surement deviné, rendons-nous dans le fichier src/clients/graphql.js pour implémenter la fonction addNewComment :

    export async function addNewComment(newPost) {}
    Découvrir la solution ici
    export async function addNewComment(comment, postId) {
        const {
            data: {
            data: { createComment }
            }
        } = await axios.post("/graphql", {
            query: `mutation createComment($postId:ID!, $content: String!) {
            createComment(postId: $postId, content: $content) {
                id
                content
            }
            }`,
            variables: {
                postId,
                content: comment.content
            }
        });
        return createComment;
    }
    

Création d'un post via GraphQL

Allons plus loin de la mutation avec la création d'un Post.

Présentation des points abordés : https://slides.com/mbreton/graphql-workshop#/4

Énoncé :

  1. Rendez-vous dans server/route/graphql.js. Ajouter une nouvelle Mutation dans laquelle il y aura une opération createPost prenant en paramètre un newPost de type PostInput, input que vous aurez créé juste avant et qui contiendra le title et le content sous forme de text.

    Découvrir la solution ici
    const typeDefs = `
        type Post {
            id: ID!
            title: String!
            content: String!
            comments: [Comment]!
        }
        input PostInput {
            title: String!,
            content: String!
        }
        type Comment {
            id: ID!
            content: String!
        }
        type Query {
            posts: [Post]!
        }
        type Mutation {
            createComment(postId:ID!, content: String!): Comment
            createPost(newPost: PostInput): Post
        }
    `);
    
  2. Comme pour l'ajout de commentaire, on ajoute le resolver correspondant. Cette opération pourra s'appuyer sur la fonction addNewPost du service server/service/index.js.

    Découvrir la solution ici
    const resolvers = {
        Query: {
            posts() {
                return service.getPosts();
            }
        },
        Mutation: {
            createComment(parentValue, args) {
                return service.addNewCommentFor(args.postId, args.content);
            },
            createPost(parentValue, args) {
                return service.addNewPost(args.newPost);
            }
        },
        Post: {
            comments(post) {
                return service.getCommentsFor(post.id);
            }
        }
    };
    
  3. Retournez dans GraphiQL, et formulez une requête graphql pour tester la mutation mise en place côté serveur. Vous devriez recevoir un résultat du type (en fonction des champs que choisissez de récupérer):

    {
      "data": {
        "createPost": {
          "id": "f1fbde00-696c-11e8-b3cf-bf211aef93e8",
          "title": "test",
          "content": "contentdzdzdz"
        }
      }
    }
    Découvrir la solution ici
  4. Plus la peine de vous le dire, rendons-nous dans le fichier src/clients/graphql.js pour implémenter la fonction addNewPost :

    export async function addNewPost(newPost) {}
    Découvrir la solution ici
    export async function addNewPost(newPost) {
        await axios.post("/graphql", {
            query: `mutation createPost($newPost: PostInput!) {
                createPost(newPost: $newPost) {
                    id,
                    title,
                    content
                }
            }`,
            variables: {
                newPost
            }
        });
    }
    

Conclusion

Le workshop est terminé, merci de votre participation.

Points à conclure https://slides.com/mbreton/graphql-workshop#/5