API : REST vs GraphQL
  • Advertising
  • Branding
  • Digital
  • Culture Pix

Dans un précédent article, nous avons découvert l’importance et l’omniprésence des API dans l’informatique et plus particulièrement le web. Cet article prend pour exemple une API RESTful, mais depuis quelques années, une alternative du nom de GraphQL fait peser la balance.

Alors, qui de REST ou de GraphQL fait l’unanimité en 2023 ?

Dans cet article, le but sera de montrer la différence entre ces deux solutions pour obtenir le même résultat, et pour cela il faudra lire un peu de code ! Mais pas de panique, même sans aucune connaissance de JavaScript, avec quelques notions d’anglais ce code devrait être compréhensible. Comme dirait Cory House : “Code is like humor. When you have to explain it, it’s bad. ».

API RESTful : le plus populaire

REST est la norme depuis le début des années 2000, introduite par les grands sites de l’époque comme eBay ou Flickr. Il faut prendre en compte que ce type d’API est le plus populaire et adopté par le plus grand nombre, même en 2023.

Sur le principe, un endpoint est donné à une URL précise, par exemple https://api.themoviedb.org/3/movie/11. Dans cet exemple, il s’agit d’une requête GET. Pour qu’elle fonctionne, il faut lui ajouter le paramètre “api_key” qui contient la clé API obtenu depuis themoviedb.org.

Cet endpoint a été mis en place par TheMovieDB, une API gratuite et open-source dont les données sont ajoutées par la communauté, afin de renvoyer toutes les informations du film qui répond à l’ID demandé. Il retourne toujours la même chose, comme prévu dans sa documentation : le nom du film, sa popularité, ses genres, son budget et d’autres informations. Un premier constat est que cet endpoint retourne beaucoup trop de données pour notre utilisation. Ce superflu de données restera pourtant présent dans toutes les requêtes, alourdissant le poids de la page web. Au contraire, si des données sont manquantes, il faudra faire une deuxième requête pour les récupérer. C’est notamment le cas ici puisque tous les crédits du film se trouvent dans un autre endpoint, notamment les acteurs et le réalisateur.

Utilisation d’une API REST en front-end : films avec TMDB

Pour cet exemple, nous voulons réutiliser l’API présenté ci-dessus, en récupérer les informations, et les afficher dans un format plus conventionnel, comme un site de présentation de fiches de film pourrait le faire. En quelques lignes de code, nous aurons alors une page web qui affichera, quel que soit le film demandé, les dernières informations les plus récentes, même pour des films qui n’existent pas encore.

Pour ce faire, nous partons d’une composition classique en React, d’abord en créant l’application rapidement à l’aide du framework Vite à partir de la ligne de commande :

npm create vite@latest my-react-app -- --template react
npm install react-router-dom

Puis en développant notre composant nommé ./src/MovieComponent.js dans lequel nous effectuons notre requête API puis affichons les données reçues.

Pour ce faire, utiliser la méthode fetch() qui contacte l’URL cité précédemment puis récupérer la réponse JSON à l’aide d’un promise chaining (fait ceci, puis cela…) qui permet de gérer les erreurs également. Stocker cette réponse dans la variable dataMovie. Répéter l’opération pour récupérer les crédits du film, cette fois sur l’endpoint /movie/credits/{id}. La réponse étant très étoffée, tronquer la réponse à 5 éléments seulement à l’aide de slice(). Pour rendre le tout plus dynamique, utiliser une bibliothèque de routing comme React Router afin de générer une page différente en précisant l’ID du film directement dans l’URL, par exemple /movie/11.

import React, { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';

const MovieComponent = () => {
    const [dataMovie, setDataMovie] = useState([]);
    const [dataActors, setDataActors] = useState([]);
    const apiKey = 'api-key';
    const baseUrl = 'https://api.themoviedb.org/3';

    useEffect(() => {
if (!movieId) {
console.log('No movie ID provided');
return;
}
        fetch(`${baseUrl}/movie/${movieId}?api_key=${apiKey}`)
            .then((response) => response.json())
            .then((responseData) => {
                setDataMovie(responseData);
            })
            .catch((error) => {
                console.log(error);
            });

        fetch(baseUrl + '/movie/11/credits?api_key=' + apiKey)
            .then((response) => response.json())
            .then((responseData) => {
                const top5Actors = responseData.sortedActors.slice(0, 5);
                setDataActors(top5Actors)
            })
            .catch((error) => {
                console.log(error);
            });
    }, []);

Enfin, générer le HTML puis récupérer les informations stockées dans les variables, par exemple pour afficher le titre du film : dataMovie.title. L’objet dataMovie contient toutes les données relatives à un seul film, et chaque propriété de cet objet est accessible via cette notation qui correspond à l’accesseur de propriétés en JavaScript.

return (
        <div>
            <div class="movie-container">
                <h1 class="movie-title">{dataMovie.title}</h1>
                <img class="movie-poster" src={'https://image.tmdb.org/t/p/original/' + dataMovie.poster_path} alt="Movie Poster" width="500"></img>
                <div class="movie-description">
                    <p>{dataMovie.overview}</p>
                </div>
                <div class="movie-details">
                    <p><strong>Release Date:</strong> {dataMovie.release_date}</p>
                    <p><strong>Runtime:</strong> {dataMovie.runtime} minutes</p>
                    <p><strong>IMDb Rating:</strong> {dataMovie.vote_average}</p>
                </div>
                <div class="movie-casting">
                    <p><strong>Acteurs principaux:</strong></p>
                    <ul>
                        {dataActors.map(function (data) {
                            return (
                                <li>
                                    {data.name}
                                </li>
                            );
                        })}
                    </ul>
                </div>
            </div>
        </div>
    );
};

export default MovieComponent;

Charger ce nouveau composant dans la première page de l’application : ./src/App.js

import React from 'react';
import './App.css';
import MovieComponent from './MovieComponent';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <MovieComponent />
      </header>
    </div>
  );
}

export default App;

Lancer l’application puis admirer le résultat à l’adresse http://localhost:5173/movie/11 :

npm run dev

Bien que cet exemple soit commun, nous faisons face à plusieurs problèmes qui obligent à un traitement lourd sur le front-end :

  • Premièrement, deux requêtes sont nécessaires pour le peu d’informations reçues, et il en faudrait autant d’autres pour afficher les bandes annonces, les avis ou encore la date de publication.
  • Nous devons récupérer tous les acteurs présents dans le film et n’en garder qu’un certain nombre arbitraire. Rien ne nous indique cependant comment sont triés les artistes et à partir de combien nous n’aurons plus affaire à des acteurs principaux.

Ce type de problématiques est inhérent à l’utilisation d’une API, qui bien souvent est créée par un service externe à l’attention d’un large public avec des intentions diverses et variées.

GraphQL

Développée par Facebook, GraphQL est une alternative récente et open-source qui est basée sur le même principe d’endpoint mais cette fois : une seule et même route sera utilisée pour renvoyer différentes informations. Cette route accepte de préférence des requêtes POST (même si GET est également possible) et a souvent un nom générique tel que /graphql. Elle est destinée à recevoir des informations dans un langage propre appelé GraphQL.

Ces informations vont permettre d’affiner le retour obtenu et donc de récupérer exactement les données nécessaire, sans superflu.

Mise en place de GraphQL

Pour utiliser à la place GraphQL, le front et le back du site doivent être modifiés.

Premièrement, un serveur GraphQL doit être mis en place. Par exemple pour Node à l’aide de la librairie GraphQL.js et du package open-source Apollo Server (très utile et populaire) :

npm install @apollo/server graphql

Il faut ensuite définir le schéma GraphQL : une définition formelle des types de données et des relations notamment. Il fait office de documentation sans avoir à s’en soucier.

const { ApolloServer, gql } = require("apollo-server-express");

const typeDefs = gql`
type Movie {
id: ID!

# General Details

name: String!
tagline: String
overview: String!
country: [Country!]!
languages: [Language!]!
status: ReleaseStatus!
genres: [Genre!]!
keywords: [Keyword!]!
releaseDate: DateTime
runtime: Int
budget: Int!
revenue: String!
adult: Boolean!

# People & Companies

cast(limit: Int): [Credit!]!
crew(limit: Int): [Credit!]!
productionCompanies: [Company!]!
}
`;

Puis des résolveurs s’occuperont de déterminer comment récupérer les données réelles, ce sont eux qui transforment les requêtes GraphQL en données JSON.

Enfin, il faut exposer un endpoint, souvent nommé /graphql, sur lequel les requêtes POST seront envoyées. Et oui, un seul !

const express = require("express");
const { ApolloServer, gql } = require("apollo-server-express");

const typeDefs = gql`
  type Movie {
    id: ID!
    name: String!
    overview: String!
    genres: [Genre!]!
    releaseDate: DateTime
    runtime: Int
    cast(limit: Int): [Credit!]!
    crew(limit: Int): [Credit!]!
  }
`;

const resolvers = {
  Movie: {
    id: (movie) => movie.id,
    name: (movie) => movie.name,
    overview: (movie) => movie.overview,
    genres: (movie) => {
      return movie.genres;
    },
    releaseDate: (movie) => movie.releaseDate,
    runtime: (movie) => movie.runtime,
    budget: (movie) => movie.budget,
    cast: (movie, args) => {
      const limit = args.limit || movie.cast.length;
      return movie.cast.slice(0, limit);
    },
    crew: (movie, args) => {
      const limit = args.limit || movie.crew.length;
      return movie.crew.slice(0, limit);
    },
  },
};

const server = new ApolloServer({ typeDefs, resolvers });

const app = express();

server.applyMiddleware({ app, path: "/graphql" });

const PORT = process.env.PORT || 4000;

app.listen(PORT, () => {
  console.log(`Serveur GraphQL prêt à l'écoute sur le port ${PORT}`);
});

Travailler ses requêtes

Du côté front, le format des requêtes est complètement différent. Il faut préciser à GraphQL le champ puis les propriétés voulues, sous ce format :

{
    field(key: "value") {
        id
        name
    }
}

Entre parenthèses, il est possible de préciser des paramètres pour cibler moins de données, par exemple un id pour trouver un élément précis, ou encore des filtres plus complexes, une pagination ou un ordre des données. Bien sûr, le résolveur doit être adapté pour ces paramètres.

Dans le cas où une collection serait utilisée à plusieurs reprises, GraphQL permet de créer des fragments afin d’éviter les répétitions.

fragment filmAttr on Film {
    title
    director
    producers
    releaseDate
}
{
    allFilms {
    films {
        …filmAttr
        }
    }
}

S’entraîner avec GraphQL

Pour éviter de mettre en place notre propre serveur dans cet exemple, nous utilisons ce playground GraphQL pour TMDB prêt à l’emploi : https://github.com/Saeris/tmdb-api

Il nous suffit de cloner le projet puis de le lancer :

git clone [email protected]:Saeris/tmdb-api.git 
cd tmdb-api
yarn install
yarn start

Ajouter la clé API TMDB dans le fichier .env sous ce format :

MOVIE_DB_API_V3_KEY=<api-key>

Puis accéder à l’adresse locale : http://localhost:1337/.netlify/functions/tmdb-api/dev

Il ne reste plus qu’à rédiger la requête selon notre besoin. Dans notre cas, nous ne voulons que les informations suivantes : nom du film, description, date de sortie, durée, note et 5 acteurs avec nom et pourquoi pas : le rôle. Voici notre demande écrite en GraphQL :

query getMovie {
    movie(id: 11) {
        id
        name
        overview
        releaseDate
        runtime
        score
        cast(limit: 5) {
            person {
            name
            }
            role {
                … on Cast {
                character
                }
            }
        }
    }
}

Grâce à cette demande précise, le retour est beaucoup plus concis et convient parfaitement à notre besoin, ni plus ni moins :

"data": {
    "movie": {
        "id": "11",
        "name": "Star Wars",
        "overview": "Princess Leia is captured and held hostage by the evil Imperial forces in their effort to take over the galactic Empire. Venturesome Luke Skywalker and dashing captain Han Solo team together with the loveable robot duo R2-D2 and C-3PO to rescue the beautiful princess and restore peace and justice in the Empire.",
        "releaseDate": "1977-05-24T22:00:00.000Z",
        "runtime": 121,
        "score": 8.2,
        "cast": [
            {
                "person": {
                    "name": "Mark Hamill"
                },
                "role": {
                    "character": "Luke Skywalker"
                }
            },
            {
                "person": {
                    "name": "Harrison Ford"
                },
                "role": {
                    "character": "Han Solo"
                }
            },
            {
                "person": {
                    "name": "Carrie Fisher"
                },
                "role": {
                    "character": "Princess Leia Organa"
                }
            },
            {
                "person": {
                    "name": "Peter Cushing"
                },
                "role": {
                    "character": "Grand Moff Tarkin"
                }
            },
            {
                "person": {
                    "name": "Alec Guinness"
                },
                "role": {
                    "character": "Obi-Wan \"Ben\" Kenobi"
                }
            }
        ]
    }
}

Il ne nous resterait qu’à récupérer ces données et les ajouter dans le HTML plus facilement que la première méthode avec REST.

Comme d’habitude, il faut faire un choix. Pourquoi ne pas choisir la norme que tout le monde utilise ? GraphQL semble plus économique et efficace mais il amène aussi son lot de problèmes : il est plus compliqué à mettre en place et impose de maîtriser un nouveau langage pour l’utiliser au maximum de ses capacités. À vous d’évaluer si ce temps est compensé par la rapidité sur les temps de requêtes !

Look at me* ou comment définir un plan de promotion
Un projet ? Contactez-nous !
Contactez-nous ! Un projet ?

À propos de nous

Agence de conseil et création en communication et marketing basée à Nice et Paris, notre approche unique allie stratégie, design et technologie pour façonner des plateformes e-commerce, expériences de marque, applications mobiles et autres projet digital ou print sur-mesure.

Depuis 0 ans, nous mobilisons nos savoir-faire au service de nos clients pour concevoir des solutions esthétiques et performantes.

Contact

NICE

16, avenue Fragonard
06100 NICE
04 93 87 03 19

PARIS

6, rue des Quatre Vents
75006 PARIS
01 42 33 58 83

Layer 1 Voir Voir
Pix Associates - Logo blanc

Pour une expérience optimale, veuillez utiliser votre mobile ou tablette en mode portrait.