API REST - L'API doit-elle renvoyer des objets JSON imbriqués?

38

En ce qui concerne les API JSON, est-il recommandé d’aplanir les réponses et d’éviter les objets JSON imbriqués?

Par exemple, disons que nous avons une API similaire à IMDb mais pour les jeux vidéo. Il existe plusieurs entités, Game, Platform, ESRBRating et GamePlatformMap, qui mappent les jeux et les plates-formes.

Disons que vous demandez / jeu / 1 qui va chercher le jeu avec l'ID 1 et qu'il retournera l'objet du jeu avec les plateformes et esrbRating imbriquées.

{
  "id": 1,
  "title": "Game A",
  "publisher": "Publisher ABC",
  "developer": "Developer DEF",
  "releaseDate": "2015-01-01",
  "platforms": [
    {"id":1,"name":"Xbox"},
    {"id":2,"name":"Playstation"}
  ],
  "esrbRating": {
    "id": 1,
    "code": "E",
    "name": "Everyone"
  }
}

Si vous utilisez quelque chose comme JPA / Hibernate, il peut le faire automatiquement s'il est défini sur FETCH.EAGER.

L'autre option consiste à simplement utiliser l'API et à ajouter davantage de points d'extrémité.

Dans ce cas, lorsque / game / 1 est demandé, seul l'objet game est renvoyé.

{
  "id": 1,
  "title": "Game A",
  "publisher": "Publisher ABC",
  "developer": "Developer DEF",
  "releaseDate": "2015-01-01",
}

Si vous souhaitez les plates-formes et / ou ESRBRating, vous devez appeler le numéro suivant:

/ jeu / 1 / plateforme / jeu / 1 / esrb

Cette méthode semble potentiellement ajouter plusieurs appels supplémentaires au serveur, en fonction des données dont le client a besoin et du moment où il en a besoin.

Il y avait une dernière pensée que j'avais où vous auriez quelque chose comme ceci retourné.

{
  "id": 1,
  "title": "Game A",
  "publisher": "Publisher ABC",
  "developer": "Developer DEF",
  "releaseDate": "2015-01-01",
  "platforms": ["Xbox","Playstation"]
}

Toutefois, cela suppose qu'ils n'ont pas besoin des ID ou de toute autre information pouvant être associée à ces objets de plate-forme.

Je demande en général quel est le meilleur moyen de structurer vos objets JSON renvoyés par votre API. Devriez-vous essayer de rester aussi proche que possible de vos entités, ou est-il préférable d'utiliser des objets de domaine ou des objets de transfert de données? Je comprends que les méthodes auront des compromis, soit plus de travail sur la couche d'accès aux données, soit plus de travail pour le client.

J'aimerais également entendre une réponse relative à l'utilisation de Spring MVC en tant que technologie de base pour l'API, avec JPA / Hibernate ou MyBatis pour la persistance.

Greyfox
la source
6
Quelles objections avez-vous, le cas échéant, à renvoyer des objets incorporés? Renvoyer des objets incorporés individuellement à partir de différents points de terminaison va devenir assez ennuyeux (sans parler de la lenteur).
Robert Harvey
1
Personnellement je n'ai aucune objection à cela. Je ne suis tout simplement pas au courant de ce qui est considéré comme des meilleures pratiques. Un collègue affirme que travailler avec des objets incorporés dans AngularJS n’est pas une tâche simple et que j’ai envie que l’application Ember of AngularJS consomme l’API. Je ne connais pas suffisamment Angular ou Ember pour savoir si cela aura un impact ou non.
Greyfox
3
La réponse dépendra si vous souhaitez renvoyer des objets de domaine, des objets DTO, des objets ViewModel ou des objets KitchenSink. L'objet que vous retournez sera très probablement déterminé par les besoins de votre application et par son comportement sur Internet. Exemple: si vous essayez de remplir une page Web avec les données d'une facture, vous retournerez probablement un objet contenant tout ce dont vous avez besoin (à moins que vous ne prévoyiez AJAX dans les éléments de campagne ou quelque chose du genre).
Robert Harvey
Quel est le cas lorsque vous demandez un jeu, vous voudrez probablement connaître les genres, les plates-formes et ESRBRating. Ça a du sens. En termes de conception d'un point de vue Java, recommanderiez-vous un package Entity contenant des entités JPA, puis un package de domaine contenant les objets métier / le DTO renvoyés à l'utilisateur?
Greyfox
1
Les appels sur le serveur sont chers. Une API qui vous oblige à envoyer des données à l'aide de plusieurs appels sera plus lente qu'une API qui vous permet de tout obtenir en un seul appel, souvent même lorsque ce dernier renvoie des informations non nécessaires.
Gort le robot

Réponses:

11

Une autre alternative (en utilisant HATEOAS). C’est simple, dans la pratique, vous ajoutez une balise links dans le JSON en fonction de votre utilisation de HATEOAS.

http://api.example.com/games/1:

{
  "id": 1,
  "title": "Game A",
  "publisher": "Publisher ABC",
  "developer": "Developer DEF",
  "releaseDate": "2015-01-01",
  "platforms": [
    {"_self": "http://api.example.com/games/1/platforms/53", "name": "Playstation"},
    {"_self": "http://api.example.com/games/1/platforms/34", "name": "Xbox"},
  ]
}

http://api.example.com/games/1/platforms/34:

{
  "id": 34,
  "title": "Xbox",
  "publisher": "Microsoft",
  "releaseDate": "2015-01-01",
  "testReport": "http://api.example.com/games/1/platforms/34/reports/84848.pdf",
  "forms": [
    {"type": "edit", "fields: [] },
  ]
}

Vous pouvez bien sûr intégrer toutes les données dans toutes les listes, mais ce sera probablement trop de données. De cette façon, vous pouvez incorporer les données requises, puis en charger davantage si vous voulez vraiment les utiliser.

L'implémentation technique peut contenir une mise en cache. Vous pouvez mettre en cache les liens et les noms de plates-formes dans l'objet de jeu et l'envoyer instantanément sans avoir à charger l'API de plates-formes. Ensuite, si nécessaire, vous pouvez le charger.

Vous voyez par exemple que j'ai ajouté des informations de formulaire. J'ai fait cela pour vous montrer qu'il peut y avoir beaucoup plus d'informations dans un objet json détaillé que vous ne voudriez même charger dans la liste des jeux.

Luc Franken
la source
Je ne pense pas que ce soit techniquement HATEOS puisqu'il n'y a pas d'état.
RibaldEddie
Oui, je ne suis pas sûr du mot exact sur ce processus. HATEOS en général est utilisé pour lier des API restantes, mais je suis d'accord que cela a également à voir avec l'état. Bien que l'idée de la mise en œuvre sera la même. Ici, vous voyez un peu plus sur la façon dont il peut être utilisé par un exemple: stormpath.com/blog/linking-and-resource-expansion-rest-api-tips
Luc Franken
C'est une bonne idée quand même!
RibaldEddie
1
Si vous développez une API où il y a une cohésion entre le client et l'API elle-même (disons une API interne), il pourrait être plus logique de renvoyer une réponse imbriquée (ou aplatie) plutôt que de fournir des liens vers une autre ressource, ce qui signifie davantage de demandes d'API. qui peut être indésirable.
Bruno
@bruno oui mais avec une limite: sur des systèmes plus importants, vous ne pouvez pas ou ne voulez pas fournir tous les objets liés dans leur intégralité. Les champs que vous incluez par défaut sont arbitraires, vous pouvez les sélectionner en fonction de l'utilisation de votre API. Donc, dans ce cas, vous pourriez avoir des plates-formes avec des centaines de champs, le cas d'utilisation montre une zone de sélection pour choisir une plate-forme. Il est alors logique d’inclure le nom de la plate-forme, mais elle n’a pas besoin des détails financiers de la plate-forme, par exemple.
Luc Franken
16

C'est l'une de ces questions fondamentales en matière de conception des API REST. Chaque designer se pose cette question le premier jour. Désolé mais la réponse est "ça dépend". Chaque approche a ses avantages et ses inconvénients et il vous suffit de prendre une décision et de l'accepter.

RibaldEddie
la source
11
Ce n'est pas du tout utile. OP lui-même savait que "cela dépend et que chaque approche a ses avantages et ses inconvénients". Vous devez expliquer sur quoi ça dépend ou à tout le moins donner un exemple.
Pratik Singhal
5

J'approuve l'approche présentée ici https://www.slideshare.net/stormpath/rest-jsonapis

En bref, incluez la ressource imbriquée en tant que liens dans la ressource parente, fournissez un paramètre de développement dans le noeud final parent.

À mon avis, c’est un moyen efficace et flexible dans la plupart des cas.

Wei Qiu
la source
2
J'aime cette approche. Pour ceux qui le demandent, cela commence à la diapositive 57 dans le diaporama lié.
Adam Plocher