Démarrer avec GraphQL

illustrations illustrations illustrations illustrations illustrations illustrations illustrations
post-thumb

Publié le 21 mars 2024 par Andrew Owen (7 minutes)

GraphQL est un langage de requête et de manipulation d’API. Créé par Facebook en 2012, il a été mis en open-source en 2015. En 2018, il a été transféré à la GraphQL Foundation et a introduit un langage de définition de schéma (SDL). Il semble qu’il soit en train de remplacer REST en tant que moyen standard d’exposer des API publiques. Avec REST, vous devez définir les entrées et les sorties pour chaque point de terminaison. Avec GraphQL, il n’y a qu’un seul point d’accès et vous définissez un schéma. L’utilisateur n’envoie que les données nécessaires pour obtenir ce qu’il veut en retour. Et contrairement à SQL, vous n’êtes pas limité à une seule source de données.

Cette année, j’ai dû me mettre rapidement à niveau avec GraphQL. Je pensais partir de rien, mais j’avais oublié que TinaCMS (le système de gestion de contenu sans tête que j’utilise pour ce site) l’utilisait. L’un des premiers problèmes que j’ai dû résoudre était de savoir comment générer de la documentation statique. Mes recherches limitées m’ont conduit à deux solutions possibles: SpectaQL, développé à partir du précédent DociQL, et Magidoc. Ce dernier dispose d’une fonction de recherche intégrée, ce qui m’a permis de faire mon choix. J’utilise Hugo comme générateur de site statique, donc la première chose à faire était de démarrer la version locale de TinaCMS à partir du dépôt Git de mon site:

npx tinacms dev - c "hugo server -D -p 1313"

Lorsque le serveur local fonctionne, vous pouvez accéder à GraphiQL à http://localhost:1313/admin/#/graphql. GraphiQL est une implémentation de référence du terrain de jeu de l’API GraphQL. S’il est trop basique pour vous, il existe une alternative commerciale appelée Apollo. L’implémentation de TinaCMS vous offre trois options (sélectionnées à partir des icônes sur la gauche) :

  • Docs: Documentation sur les API dans une structure arborescente.
  • History: Requêtes précédentes.
  • Queries: Constructeur de requêtes.

Après avoir jeté un coup d’œil à la section Docs de GraphQL, vous comprendrez pourquoi j’ai voulu générer un site statique. Avec Magidoc, c’est facile. J’ai ajouté ce fichier de configuration à mon dépôt Git:

export default {
  introspection: {
    type: 'url',
    url: 'http://localhost:4001/graphql',
  },
  website: {
    template: 'carbon-multi-page',
	  customStyles: ['/static/css/custom.css'],
  },
}

Ensuite, pour générer la documentation, entrez: pnpm add --global @magidoc/cli\@latest && magidoc generate (vous aurez besoin que pnpm soit installé). Par défaut, cela crée un site statique dans un dossier appelé docs. Cependant, vous ne pouvez pas simplement ouvrir le fichier index.html. Vous devez lancer le serveur avec magidoc preview et suivre le lien. Vous pouvez ajouter le dossier docs à votre fichier .gitignore. Mais en production, vous pouvez déployer en utilisant n’importe quel serveur web. La sortie est basée sur le Carbon Design System d’IBM.

Maintenant que vous disposez d’une documentation statique consultable, vous pouvez commencer à explorer votre schéma. Les schemas GraphQL peuvent être écrits dans n’importe quel langage de programmation qui implémente le système de type. Avec TinaCMS, cela signifie TypeScript ou JavaScript. Voici un extrait de mon fichier Tina config.js:

    schema: {
      collections: [
        {
          name: "blog",
          format: "md",
          label: "Blog",
          path: "content/blog",
          defaultItem: () => {
            return {
              draft: true,
            }
          },
          fields: [
            {
              name: "draft",
              type: "boolean",
              label: "Draft",
              required: true,
            },
            {
              name: "title",
              type: "string",
              label: "Title",
              isTitle: true,
              required: true,
            },
            {
              name: "date",
              type: "datetime",
              label: "Date",
            },
            {
              name: "description",
              type: "string",
              label: "Description",
            },
            {
              name: "image",
              type: "image",
              label: "Image",
            },
            {
              name: "image_license",
              type: "string",
              label: "Image License",
            },
            {
              name: 'tags',
              type: 'string',
              label: 'Tags',
              list: true,
            },
            {
              name: 'body',
              type: 'rich-text',
              isBody: true,
              label: "Body",
              templates: [
                {
                  name: 'shortcode',
                  label: 'shortcode',
                  match: {

This creates a schema type called Blog with the fields:

  • draft: Boolean!
  • title: String!
  • date: String
  • description: String
  • image: String
  • image_license: String
  • tags: [String]
  • body: JSON
  • id: ID!
  • _sys: SystemInfo!
  • _values: JSON!

Les champs peuvent être scalaires (Boolean, Float, Integer, String et ainsi de suite) ou complexes (contenant d’autres données). Un point d’exclamation ( ! ) signifie que le champ est obligatoire (non annulable). Les crochets signifient qu’il s’agit d’un tableau. Si le type est JSON, un ensemble de sous-champs est défini. Vous pouvez également utiliser des types précédemment définis pour créer des relations. Par exemple, si votre blog a plusieurs contributeurs, vous pouvez avoir un champ appelé author avec le type User!.

Requêtes

Comme il n’y a qu’un seul point d’accès, vous devez lui dire ce que vous voulez. Par exemple, pour obtenir la liste des collections, vous devez utiliser :

{
  collections {
    name
  }
}

Le fichier collections est le champ racine. Tout le reste est le payload. Parce que le payload ne contient que nom, la requête renvoie une liste de toutes les collections:

{
  "data": {
    "collections": [
      {"name": "blog"},
      {"name": "recipe"}
    ]
  }
}

Vous obtenez exactement la quantité d’informations que vous avez demandée dans la charge utile. Vous pouvez également passer des arguments. Par exemple, si vous voulez obtenir le titre, la date et le statut draft de cet article, vous pouvez utiliser:

{
  blog(relativePath: "getting-started-with-graphql.md") {
    title
    date
    draft
  }
}

Toutefois, en règle générale, vous devez utiliser une variable dans la requête, comme ceci:

query blog($relativePath: String!) {
  blog(relativePath: $relativePath) {
    title
    date
    draft
  }
}

Les variables ont un préfixe ( $ ). Le type (String dans cet exemple) est défini dans la requête. La variable est passée dans les paramètres blog.

Mutations

La requête est l’équivalent de GET dans REST ou de read dans la terminologie traditionnelle des données. Pour create, update et delete il y a mutation. La syntaxe est la même que pour les requêtes. Vous avez peut-être remarqué le type ID plus tôt. Si vous le spécifiez lors de la création d’un enregistrement, le serveur GraphQL lui donnera un identifiant unique. Vous pouvez essayer cet exemple tiré de la documenation de TinaCMS (coller aux références de Napoleon Dynamite):

mutation {
  updatePost(relativePath: "voteForPedro.json", params: {title: "Vote For Napolean Instead", category: "politics", author: "content/authors/napolean.json"}) {
    title
    category
    author {
      ... on Author {
        id
      }
    }
  }
}

Abonnements

J’ai déjà écrit sur les architectures pilotées par les événements. Les abonnements sont un moyen d’obtenir des données du serveur GraphQL chaque fois qu’un événement souscrit a lieu. Tous les serveurs GraphQL ne sont pas configurés pour prendre en charge les abonnements websocket, et c’est le cas de mon instance TinaCMS. Mais si c’était le cas, voici à quoi ressemblerait une requête pour être informé de la création d’un nouvel article de blog:

subscription {
  newBlog {
    title
  }
}

Scalaires personnalisés

Cinq mois après avoir écrit cet article, j’ai rencontré un problème avec Magidoc. La construction de la documentation échouait car il manquait des définitions pour les scalaires personnalisés. Au début, j’ai pensé qu’il s’agissait d’un bug, mais il s’avère que la version précédente construisait les documents même si les définitions étaient manquantes. Vous n’avez pas besoin de fournir des définitions pour les scalaires prédéfinis (String, Int, Float, Boolean et ID). Mais si vous avez des scalaires personnalisés dans votre schéma, vous devez fournir un exemple pour chacun d’entre eux au format JSON dans le fichier de configuration de Magidoc. Ces définitions vont sous options dans la section website:

Traduit avec DeepL.com (version gratuite)

  website: {
    template: 'carbon-multi-page',
	  customStyles: ['/static/css/custom.css'],
    options: {
      queryGenerationFactories: {
        accountID: "01234567-890a-bcde-f012-34567890abcd",
      }
    }
  }

Conclusion

Cet article n’a pas vocation à couvrir plus que les principes de base. Pour plus d’informations, la documentation GraphQL est un bon point de départ. TinaCMS est une bonne application pour commencer à expérimenter avec son API GraphQL. Pour les plus aventureux, Kingdom Orjiewuru a écrit la première partie d’un article sur la construction d’un gestionnaire de documents simple avec GraphQL.