keyboard_arrow_up

Drupal 8 / JSON:API - Présentation en 10 minutes

Rédigé par Sylvain Lavielle
Développeur web freelance expert Drupal sur Toulouse

Le 11/10/2019

JSON:API est une API web normalisée semblable à GraphQL et dont on peut trouver la spécification complète sur jsonapi.org. Tout comme GraphQL elle est destinée à servir d'interface entre un back-end et un client (application mobile, application RIA en React, Vue.js ou Angular). Le format de réponse de ces deux API est le JSON.

Le module Drupal json API permet donc d'accéder aux données stockées dans les entités Drupal via JSON:API sans qu'aucune configuration spécifique ne soit nécessaire et fait désormais partie du core Drupal 8 depuis la version 8.7. Vous pourrez trouver une excellente présentation des différents concepts de JSON:API et de ce module sous forme de screencast réalisés par Mateu

Une requête JSON:API peut permettre de récupérer mais également de créer, mettre à jour ou supprimer des entités dans Drupal. L'objectif de cet article étant de donner rapidement (en 10 minutes) quelques clefs pour prendre en main JSON:API. Nous allons exclusivement  nous intéresser dans cet article à la récupération de données.

Pour tester les requêtes JSON:API, nous allons utiliser la démo Drupal Umami - un faux magazine culinaire en ligne qui est une démo classique de Drupal - et dont une instance est disponible en ligne sur https://using-drupal.amazee.io

Quelques principes de base

Une requête JSON:API permettant de récupérer des données peut être réalisée via une simple l'URL en méthode GET. Les paramètres de la requête les plus basiques sont passés sur le chemin de l'URL et d'autres paramètres plus évolués sont passés par la query string de l'URL.

Lorsqu'on utilise JSON:API en consultation, on veut en général récupérer soit des collections d'entités soit une entité spécifique.

Récupérer une collection d'entités

Modèle URL : /jsonapi/<entity-type>/<bundle>

  • <entity-type> : Le type le l'entité Drupal à requêter. Exemple : node, user, etc.
  • <bundle> : le bundle de l'entité. ex : Exemple : article, page, recipe,  etc.

Exemple :

Récupérer une entité spécifique

Modèle URL : /jsonapi/<entity-type>/<bundle>/<uuid>

  • <uuid> : L'uuid de l'entité

Exemple :

Format de la Réponse

Une réponse JSON:API à ces 2 types de requêtes sera toujours structurée de cette façon :

{
  "data": {
    "type": "node--recipe",
    "id": "1",
    "attributes": {
      // ... this article's attributes
    },
    "relationships": {
      // ... this article's relationships
    }
  }
}
  • Si la requête récupère une collection, data contiendra alors une liste d'objets et non directement un objet comme dans l'exemple ci-dessus.
  • attributes est un objet contenant les champs de valeurs (par exemple des champs de type texte, entier, etc.)
  • relationships est un objet contenant les champs qui ont une relation avec une autre entité (par exemple des champs de type entity reference pointant vers une taxonomie, une image, etc.)
  • Les objets localisés dans relationships sont des références vers d'autres entités et ne contiennent pas les valeurs des champs de l'entité. Pour obtenir ces valeurs il faudra utiliser l'inclusion.

La pagination

Objectif : Permettre de ne récupérer qu'une partie des résultats (page) .

Modèle URL : <path>?page[offset]=<result-offset>&page[limit]=<result-limit>

  • <result-offset> : position dans la liste totale de résultats du premier résultat à renvoyer
  • <result-limit> : nombre de résultats maximum dans la réponse

Exemple :

L'ordonnancement

Objectif : Permettre de trier les résultats obtenus.

Modèle URL : <path>?sort=<sort-name>,<sort-name>,...

  • <sort-name> : Nom de la propriété des entités (data[].attributes) sur lesquelles effectuer les tris.

Exemple :

Les Filtres

Objectif : Permettre de filtrer les résultats en fonction de certaines conditions. La spécification JSON:API est très peu précise concernant les filtres et indique que la façon dont les filtres sont définis dépend de l'implémentation côté serveur. Le système de filtres utilisé par le module JSON:API Drupal lui est donc spécifique et est documenté dans la documentation du module JSON:API relative aux filtres.

Filtres simples

Dans sa forme la plus simple, un filtre utilise à minima deux paramètres : [condition][path] et [condition][value].

Modèle URL : <path>?filter[<filter-id>][condition][path]=<data-path>&filter[<filter-id>][condition][value]=<data-value>

  • <filter-id> est un nom arbitraire donné au filtre qui permettra de l'identifier clairement,
  • <data-path> est le nom de la propriété à tester dans l'objet attributes ou relationships. On parle de chemin car, en fait, on peut également tester les propriétés des relations si celles-ci sont incluses (voir inclusion),
  • <data-value> est la valeur à tester.

Exemple :

Filtres complexes

Il est possible de réaliser des opérations plus complexes en utilisant des [condition][operator]. Voici un exemple permettant de sélectionner les recettes de niveau "easy" et "medium"

Exemple :

Groupes

Il est également possible de combiner les conditions dans des groupes utilisant des opérateurs logiques ET et OU. L'écriture et la logique sont un peu particulières mais ne sont pas très compliquées à comprendre. La documentation contient quelques bons exemples de mises en oeuvre de groupes de conditions

Les Sparse fieldsets

Objectif : Permettre de n'afficher que certaines données. Par défaut, tous les attributs et les relations d'une entité sont retournés.

Modèle URL : <path>?fields[<entity-type>]=<attribute-or-relationship-prop>, <attribute-or-relationship-prop>, ...

  • <entity-type> : type de l'entité
  • <attribute-or-relationship-prop> nom de la propriété à faire figurer dans les objets attributes ou  relationships de l'entité

Exemple :

L'inclusion

Objectif : L'inclusion permet de récupérer des valeurs supplémentaires contenues dans les relations

Modèle URL : <path>?include=<relation-key>

  • <relation-key> : nom de la propriété dans l'objet relationships

Exemple :

Réponse JSON:API avec inclusions

Ajouter des inclusions va modifier la réponse JSON. Une liste d'objets (included) est ajoutée au même niveau que data.

{
  "data": [ 
    // main entities data 
  ]
  "included" : [
    // included entities data 
  ]
}

included est structuré exactement de la même façon que data mais les entités qui y sont présentes ne sont que des entités ayant fait l'objet d'une inclusion à partir des relationships des entités principales localisées dans data .

Les entités chargées dans included seront bien sûr limitées à celles qui sont référencées dans les entités principales. Cependant, pour faire la liaison entre les deux entités (principale et incluse) il faudra utiliser l'uuid de l'entité incluse ... Cette liaison va demander de faire une manipulation particulière lors du traitement du json par l'application cliente. Personnellement, je trouve ce choix un peu discutable et j'ai une petite préférence pour l'approche GraphQL par fragments. Il est vrai que dans GraphQL les données d'une entité en relation avec l'entité principale peuvent donner lieu à une duplication de données (si une entité dans un fragment est référencée dans plusieurs entités principales, les valeurs sont dupliquées dans la réponse json) mais au moins, l'information est directement utilisable.

Limiter les valeurs présentes dans les entités incluses

Tout comme il est possible de limiter les valeurs présentes dans l'entité principale, il est possible de limiter celles présentes dans les entités incluses

Exemple :

Filtrer sur les valeurs d'une inclusion

Il est également possible de filtrer en fonction de valeurs présentes dans les entités incluses.

Exemple :

Conclusion

Cet article a pour but de présenter les concepts principaux de JSON:API de façon rapide et par l'exemple, en se limitant à l'aspect récupération des données, ce qui est suffisant pour un certain nombre de projets.

Cependant, JSON:API dispose également de capacités de création, modification et suppression d'entités mais pour les présenter, il faudrait disposer d'un peu plus de 10 minutes. Peut-être bien un sujet pour un prochain article.

 

 

 

 

Sujets abordés dans cet article