Serveur d'impression

Introspected REST: une alternative à REST et GraphQL – Serveur d’impression

Le 3 mai 2019 - 118 minutes de lecture

Dans ce manifeste, nous allons donner une définition précise de ce que REST est, selon Roy,
et voir la majorité des API et spécifications API (JSONAPI, HAL, etc.) ne pas suivre ce modèle.
Nous verrons quels problèmes une API RESTful apporte et pourquoi les concepteurs d’API sont constamment
en évitant de l’utiliser, mais en proposant des solutions à mi-parcours ou un repli sur des solutions alternatives.
des modèles tels que RPC sur HTTP ou, récemment, GraphQL.
Ensuite, nous proposerons un nouveau modèle, REST Introspected,
qui résout les problèmes créés par REST et permet la conception d’API évolutives,
de manière beaucoup plus simple que REST conventionnel.
Dans le cadre de ce manifeste, nous présenterons également le concept de MicroTypes,
petits modules réutilisables qui composent un type de support et facilitent son évolution
et extensibilité de notre nouveau modèle.

Pour la mise en œuvre de notre nouveau modèle en HTTP, il faudra remonter dans le temps,
approfondir les RFC existantes et découvrir des concepts oubliés, tels que le contenu réactif
paramètres de négociation et de type de média, afin de plier le réseau Internet existant
l'infrastructure, qui a été principalement influencée par les concepts REST, et a appliqué avec succès notre nouveau modèle.

Sommaire

1. Définitions

Tout d’abord, quelques définitions que nous utiliserons dans le texte:

  • DU REPOS, Reposant: Le modèle que Roy a défini dans sa thèse (avec son API de post de blog doit être piloté par hypertexte).
  • Le reste: API qui suivent tous les modèles de pièces REST, sauf HATEOAS dans lequel ils supportent principalement des liens (spécifications telles que JSONAPI, HAL, etc.)
  • Agité: API ayant une API JSON simple sans aucun lien (suit un modèle REST autre que HATEOAS)
  • REST Introspecté: Les API qui suivent la définition du modèle que nous fournissons dans cette manifeste

Nous utiliserons le terme API et API en réseau de manière interchangeable.

2. Introduction

REST défini par Roy était un travail magnifique, très en avance sur son temps
ce qui nous a pris de nombreuses années pour comprendre quelles sont ses capacités.
Cependant, presque 20 ans plus tard, le modèle REST montre son âge. C’est inflexible,
difficile à mettre en œuvre, difficile à tester, avec des problèmes de performance et de mise en œuvre.
Mais le plus important, toute implémentation du modèle REST est très complexe.
Maintenant, on pourrait dire que la plupart des API ne sont pas conçues pour durer des décennies et peut-être
C’est la raison pour laquelle ce modèle n’a pas été très adopté.
Le premier est vrai, mais même si le dernier est aussi vrai, cela pourrait-il signifier que ce modèle est
ne convient pas aux API à court terme?

Nous croyons fermement que REST est bien meilleur que toute API qui ne suit pas DU REPOS des principes
(comme les API RESTly), même pour les API à court terme.
Les services en réseau ont des caractéristiques très particulières que, jusqu'à présent, seuls REST et GraphQL les ont entièrement pris en charge.
Il est essentiel de pouvoir faire évoluer notre API sans casser les clients.

Imaginez le scénario suivant: nous avons créé un réseau social en ligne et une application iOS qui communique avec l'API sur notre backend.
Maintenant, imaginez qu’après une réunion d’entreprise, notre PDG ait besoin que nous apportions des modifications minimes mais importantes à la page d’inscription: demandez à l’utilisateur
pour remplir son âge, un champ du formulaire d'inscription que nous n'avions pas auparavant.
Cela signifie essentiellement, en termes d'API, ajouter un champ supplémentaire dans l'objet accepté et obliger le client à le renseigner.
par l'utilisateur avant de l'envoyer.

Si notre API est RESTly et non REST, cela signifie que nous devons corriger le code côté iOS, le tester et envoyer une nouvelle application iOS à Apple Store.
Il faut environ une semaine à Apple pour examiner notre application. Si, pour une raison quelconque, notre application n’est pas rejetée pendant le processus de révision, notre
petit changement prendra des mesures au moins une semaine plus tard après la demande.
Si notre API était REST, cela signifierait un simple changement dans la réponse du serveur, indiquant quels champs sont requis pour soumettre le formulaire.
Le changement serait déployé 10 minutes plus tard.

Roy note dans sa thèse:

Un système qui se veut aussi durable que le Web doit être préparé au changement

– Roy Fielding

Dans le monde des startups et des entreprises axées sur l'argent, les éléments suivants seront plus attrayants:

Si vous souhaitez agir rapidement, vous devez créer une API axée sur le changement en premier.

Une API qui peut changer l'état du client sans avoir à changer ce dernier.

Étant donné que, comment pouvons-nous avoir un modèle plus simple que REST, tout en tirant la même, sinon plus, fonctionnalité de
DU REPOS?

Comme nous le montrerons, Introspected REST est un style architectural API qui résout ce problème.
Un style architectural n’est pas une implémentation et ce n’est pas non plus une spécification.
Comme le note Roy:

Un style architectural est un ensemble coordonné de contraintes architecturales qui limite
 les rôles / caractéristiques des éléments architecturaux et les relations autorisées entre ceux-ci
 éléments de toute architecture conforme à ce style.

– Roy Fielding

Introspected REST est basé sur le modèle initial de Roy, mais élimine le besoin de runtime HATEOAS.
Au lieu de cela, le client dérive l'état à la demande, en utilisant l'introspection, en récupérant les métadonnées nécessaires.
qui sont d'intérêt.
Finalement, cela apporte les mêmes avantages que le modèle de Roy, tout en étant beaucoup plus simple,
beaucoup plus souple et compatible avec toutes les API RESTly ou RESTless.

Mais parlons d’abord des services en réseau.

3. Services en réseau et API

Aujourd'hui, JSON est devenu si populaire que les développeurs oublient presque qu'il existe un tas de
protocoles en dessous.
Les développeurs oublient également que JSON n'est qu'une spécification au niveau du message, comme XML.
Ce n’est pas le seul et ce n’est certainement pas le meilleur que nous puissions utiliser.
Néanmoins, c’est simple et la simplicité est une vertu.

Bien que le modèle OSI soit le modèle conceptuel que nous utilisons pour décrire les réseaux informatiques,
avec TCP / IP suivant 5 couches d’abstraction OSI sur 7, dans notre cas, nous en ferons une description plus spécifique à l’API.
Lorsque nous souhaitons demander une ressource à une API hypermédia en réseau, nous grossièrement
avoir les niveaux suivants:

3.1. Niveau d'application

Au niveau de l’application, le client commence la négociation du contenu (ou la sélection du contenu), généralement en demandant
pour un seul type de support.
Un type de support fournit des informations sur la structure du contenu et le format du message utilisé dans les données qu'il décrit.
comme décrit par la RFC 2046.

Dans le protocole HTTP, la négociation du contenu est réalisée par un client à l'aide de l'en-tête Accept qui indique les types de support qu'il
peut comprendre, dans un ordre de préférence.
Ensuite, le serveur répond avec un type de support proposé par le client dans l'en-tête Content-Type.

application / json est un type de support qui indique que le format de données de la demande
la représentation est au format de données JSON.
Plus précisément, le type de ce type de support est application tandis que le sous-type est JSON.
JSON lui-même n'est pas un type de support mais un format de message.

Les types de support peuvent également être un peu plus complexes: application / vnd.api + json, le type de support de la spécification JSONAPI, signifie (à peu près) que

  • le type principal est application
  • le sous-type est vnd.api lequel grossièrement dénote le nom du type de média
  • la structure sous-jacente suit la sémantique JSON

En théorie, la sémantique des spécifications JSONAPI pourrait également être appliquée en utilisant XML comme format de données (comme dans le cas de HAL),
ou même YAML, mais en pratique, nous avons tendance à l'oublier et nous traitons tous les types de supports comme uniques et non composites.

Cependant, il convient également de noter que le Les types de média et la négociation de contenu en général sont
non limité à HTTP seulement
.
Bien que HTTP soit l’un des protocoles de réseau d’applications les plus populaires à l’heure actuelle, la même logique pourrait être appliquée.
dans d'autres protocoles (principalement textuels) tels que SIP, CoAP, QUIC, etc.

En résumé, la sémantique au niveau de l'application est définie par le type de média demandé et ne doit pas être étroitement couplée à la sémantique du
niveau du message (comme JSON) ou le niveau de protocole sous-jacent (comme HTTP).

3.2. Niveau du message

Au niveau du message, nous trouvons le format utilisé pour la représentation réelle.
Aujourd’hui, nous avons presque mélangé le niveau de message avec JSON, mais dans la pratique,
formats pourraient être utilisés avec succès: XML, YAML, TOML pour en nommer quelques-uns.

Le format de message utilisé est généralement décrit par le suffixe du type de support.

3.3. Niveau de protocole

Au niveau du protocole, les demandes sont généralement envoyées à l'aide du protocole HTTP.
Après tout, de nos jours, la majeure partie du développement se fait sur le Web et
HTTP est le seul protocole officiellement pris en charge par les navigateurs.

Néanmoins, il existe également d'autres protocoles similaires sans état.
QUIC est un protocole alternatif HTTP ciblé pour une faible latence et utilise UDP.
sous.
CoAP est ciblé dans l'IdO et utilise également UDP en dessous (la pile TCP / IP complète est assez lourde pour les périphériques à contraintes).
SIP est également un protocole textuel avec la même sémantique que HTTP et est utilisé en VoIP.

3.4. Niveau réseau

Enfin, au niveau du réseau, le navigateur (ou tout autre client autre que le navigateur) envoie la requête en réseau.
dans l'un des TCP, UDP, etc.

Le protocole réel dépend du protocole utilisé au niveau du protocole.

4. Modèle REST de Roy

Roy a mis au point le modèle REST afin de résoudre les problèmes liés aux propriétés uniques des services en réseau.
pendant la petite enfance d'Internet.
Lorsque nous développons une application qui sera déployée dans un environnement réseau et que d’autres services en réseau devraient y accéder,
nous devons réfléchir à son évolutivité:
si nous devons ajouter, supprimer ou modifier les fonctionnalités de cette application
nous ne pouvons pas nous attendre à ce que les services à l'autre bout parlent avec notre application d'être mis à jour par des humains.
De tels problèmes résultant des particularités des réseaux, tels que la découverte et l’évolutivité, doivent être résolus en utilisant
communication machine à machine.

Le modèle REST qui résout de tels problèmes est un style architectural qui n'est lié à aucune spécification, protocole ou format de la
les niveaux susmentionnés.

une API RESTful n'est qu'un site Web pour les utilisateurs dont le vocabulaire est limité (interaction machine à machine)

– Roy Fielding

Quand Roy parle de REST, il mentionne 5 propriétés cruciales de DU REPOS modèle:

4.1. Les méthodes d'accès ont la même sémantique pour toutes les ressources

induit visible, évolutif, disponible via des systèmes multicouches, des caches partageables et cachables

L’absence de cohérence dans l’accès impliquerait que nous ne fournissions pas une interface générique mais
nous avons des interfaces spécifiques aux ressources ou même aux objets.

En fait, une interface commune est l’une des parties les plus cruciales de REST: sans une interface uniforme commune
il serait impossible de dériver REST.

La caractéristique centrale qui distingue le style architectural REST des autres
styles basés sur le réseau est l'accent mis sur une interface uniforme entre les composants.
En appliquant le principe de génie logiciel général à l’interface composant,
l'architecture globale du système est simplifiée et la visibilité des interactions est améliorée.
Les implémentations sont découplées des services qu’elles fournissent, ce qui encourage les opérateurs indépendants.
évolutivité. Le compromis, cependant, est qu'une interface uniforme dégrade l'efficacité,
puisque les informations sont transférées sous une forme normalisée plutôt que sous une forme spécifique
aux besoins d’une application. L’interface REST est conçue pour être efficace pour
transfert de données hypermédia à gros grains, optimisation pour le cas classique du Web,
mais aboutit à une interface qui n'est pas optimale pour d'autres formes d'interaction architecturale.

Pour obtenir une interface uniforme, plusieurs contraintes architecturales sont nécessaires pour guider le comportement des composants.
REST est défini par quatre contraintes d'interface: identification des ressources; manipulation des ressources par
représentations; messages auto-descriptifs; et hypermédia en tant que moteur de l'état de l'application.

– Roy Fielding

Par la suite, les 4 contraintes suivantes, le noyau de REST, résultent des efforts déployés pour obtenir une interface uniforme entre différents composants.

4.2. Toutes les ressources importantes sont identifiées par un mécanisme d'identificateur de ressource

induit une communication simple, visible, réutilisable et sans état

Roy l'explique très bien dans sa thèse:

Une ressource est un mappage conceptuel sur un ensemble d'entités et non sur l'entité qui correspond au mappage à un moment donné.

Plus précisément, une ressource R est une fonction d’appartenance M variant dans le tempsR(t),
qui pour le temps t mappe à un ensemble d’entités, ou valeurs, qui sont équivalentes.
Les valeurs de l'ensemble peuvent être des représentations de ressources et / ou des identificateurs de ressources. […]
Certaines ressources sont statiques dans le sens où, examinées à tout moment après leur création,
ils correspondent toujours au même ensemble de valeurs.
D'autres ont un degré de variance élevé dans leur valeur au fil du temps.
La seule chose qui doit être statique pour une ressource est la sémantique du mappage,
puisque la sémantique est ce qui distingue une ressource d’une autre.

– Roy Fielding

4.3. Les ressources sont manipulées par l'échange de représentations

induit simple, visible, réutilisable, en mémoire cache et évolutif (masquage d'informations)

La représentation que nous exposons à partir de notre API publique pourrait être totalement différente de
notre mise en œuvre en interne ou comment les données sont stockées dans notre base de données.
Cela pourrait aussi être la même chose.
Néanmoins, le client attend et doit manipuler toute ressource à l'aide de la représentation.
nous exposons.

4.4. Les représentations sont échangées via des messages auto-descriptifs

induit visible, évolutif, disponible via des systèmes multicouches, des caches partageables et cachables
induit évolutif via une communication extensible

Cela signifie que les données de la réponse doivent suivre le type de média que le client
demandé et comprend.
Étant donné que le client a négocié pour ce type de média, il devrait être capable d'analyser et de comprendre n'importe quelle partie de la réponse.

L’interaction est sans état entre les demandes, les méthodes standard et les types de support sont utilisés pour
indiquer la sémantique et échanger des informations, et les réponses indiquent explicitement la possibilité de mise en cache.

– Roy Fielding

Si notre type de média est très faible (comme application / json) et nous avons besoin de fonctionnalités que le type de support ne décrit pas
Ensuite, nous devons définir un autre type de support qui décrira la nouvelle sémantique et attendre que le ou les clients intègrent les nouvelles modifications du type de support.

Briser la sémantique de notre type de support, ou simplement lui ajouter de nouvelles fonctionnalités, aura exactement le même résultat pour le client:
messages non descriptifs qui nécessiteront des informations hors bande, telles que la documentation.

4.5 L'hypertexte en tant que moteur de l'état de l'application (HATEOAS)

induit simple, visible, réutilisable et pouvant être mis en cache grâce à une intégration orientée données
induit une évolutivité (couplage lâche) via une liaison tardive des transitions d'application

C’est l’une des parties les plus mal comprises du modèle REST de Roy. L'idée ici est que,
une fois que le client et le serveur sont parvenus à un consensus sur le type de support après la négociation,
la réponse du serveur doit fournir strictement toutes les options disponibles pour le client
pour manipuler la ressource et accéder à d’autres ressources, en utilisant la sémantique définie par
le type de média accepté.

Comme le note Roy:

Une API REST doit être entrée sans connaissance préalable au-delà de l'URI initial (signet).
et un ensemble de types de supports standardisés adaptés au public cible
(c’est-à-dire qu’il devrait être compris par tout client susceptible d’utiliser l’API).

À partir de ce moment, toutes les transitions d'état de l'application doivent être pilotées par le client
sélection des choix fournis par le serveur présents dans les représentations reçues
ou implicite par la manipulation de ces représentations par l'utilisateur.

Les transitions peuvent être déterminées (ou limitées par) la connaissance du client des médias
types et mécanismes de communication des ressources, qui peuvent tous deux être améliorés
à la volée (par exemple, code à la demande).

– Roy Fielding

cependant, L’une des conditions à remplir pour qu'HATEOAS fonctionne est que le type de média lui-même doit permettre
à son vocabulaire hypermédia.

Par exemple, avec application / json Type de support cela ne fonctionnerait pas comme JSON lui-même
(application / json Le type de support n’est rien de plus qu’un JSON) ne fournit aucun de ces mécanismes.

Au lieu de cela, le serveur et le client doivent s’accorder sur un format offrant de tels mécanismes.

Malheureusement, la pratique courante est de mettre application / json dans notre en-tête Content-Type indiquant
que le type de réponse suit ce type de média, puis à l'intérieur de la réponse, nous ajoutons
sémantique concernant l'hypermédia. Ensuite, nous transmettons des informations hors bande au client,
comme la documentation, et exiger de les vérifier avant d'identifier l'analyse et l'utilisation de l'hypermédia
sémantique de notre API.

5. Clients et applications API

5.1. Responsabilités du client et de l'application

Les responsabilités du client et de l'application sont parfois mélangées.

Un client est responsable de la compréhension, de l’interaction avec l’API et de la manipulation des ressources de l’API, en fonction de la sémantique du type de support.
et runtime HATEOAS. Le client est responsable de la fourniture dans l’application de la liste des ressources disponibles dans l’API,
leurs champs, leurs capacités, les actions disponibles et tout hypermédia disponible.

La responsabilité de l'application, d'autre part, ne doit pas inclure les détails spécifiques à l'API.
Au lieu de cela, en utilisant le client, il devrait extraire tout ce dont le domaine d’application a besoin, dans les limites des capacités de l’API.

Pensez aux appareils téléphoniques résidentiels traditionnels.
Le fil téléphonique et ses signaux constituent l’API.
Le périphérique utilisé pour coder / décoder les signaux de fil est le client API.
Sur un appareil, nous pouvons exécuter notre application.
Le PSTN, le RNIS, le (A) DSL, etc. sont tous différents types de média pour la même API (signaux filaires).
Pour chacun d'eux, nous avons besoin d'un client (périphérique / modem) capable de comprendre (coder / décoder) les signaux de fil de ce type de support.
En utilisant ce client, nous pouvons créer n’importe quel type d’application, dans l’espace réalisable du type de support.
L’application ne traite pas de la sémantique de l’API, mais utilise le client pour effectuer ses tâches.

5.2. Le principe du facteur humain

Il existe deux types d’intervention humaine lors de la création d’un client API:

  • 1 fois: Programmer le client une seule fois pour comprendre correctement le type de support et laisser le
    le travail client pour toute API qui suit ce type de support même lorsque les API évoluent, étant donné qu'il est conforme aux spécifications de type de support.
    La seule chose dont le client a besoin est l'URI initial de l'API.
  • multi-pli: Programmer le client une fois pour comprendre le type de média.
    Modifiez ensuite le client pour analyser et comprendre correctement l’API à l’aide d’un contrat hors connexion.
    (documentation sur les ressources disponibles, les champs, la pagination, etc.), puis
    chaque fois que l'API évolue (par exemple, l'ajout d'une ressource ou d'un champ), reprogrammez le client en conséquence. L'étendue de l'implication humaine
    pendant cette phase dépend de la faiblesse du type de média.

Une API qui suit le modèle REST devrait être évolutive sans qu'il soit nécessaire
d’implication humaine du côté client, étant donné que le client comprend le type de média.
L’un des effets secondaires d’une telle évolutivité et d’un seul client est que la la gestion des versions ne doit pas avoir lieu dans l'URL mais dans le type de support lui-même.

La gestion des versions d'une interface n'est qu'un moyen poli de tuer les applications déployées

La raison pour créer une véritable API REST est d'obtenir une capacité d'évolution… un «v1» est un moyen majeur pour vos clients d'API, indiquant RPC / HTTP (pas REST).

Roy Fielding

6. REST appliqué dans une API moderne

Lors de l'ingénierie d'une API REST, il existe 2 approches:

  • concevoir une API spécialisée, généralement pilotée par l'interface utilisateur: les ressources et leur facilité de navigation sont étroitement associées à l'application spécifique conçue pour
  • concevoir une API générique, généralement pilotée par les données: les ressources sont plus génériques et les fonctionnalités de l’API permettent une multitude de transformations.

Les API spécialisées pourraient être plus efficaces ou présenter des caractéristiques avantageuses cruciales pour le domaine, conçues pour
car ils ne sont optimisés que pour ce cas particulier.
Cependant, ils posent des problèmes lorsqu'ils doivent être réutilisés par toute autre application ne partageant pas la même interface utilisateur.
En conséquence, ces API sont très spéciales et un peu rares.

D'autre part, les API pilotées par les données sont plus génériques et facilitent toute application pour demander les données optimisées.
(dans le cadre des capacités de l’API) pour son cas d’utilisation.
Pouvoir spécifier les besoins de notre application lors de la demande de données à partir d’une API est crucial,
surtout si notre activité dépend de la capacité d’adoption de notre API.

Pour les sous-sections suivantes, nous nous concentrerons principalement sur les API de données génériques,
Cependant, la plupart des choses mentionnées ici peuvent également être appliquées dans une API spécialisée ou pilotée par une interface utilisateur.

6.1. Exigences d'une API REST moderne

Le modèle REST est conçu pour la communication de machine à machine de tout type.
Cependant, comme cette forme de communication devient de plus en plus courante,
les clients attendent plus d'options (capacités) de la part du serveur pour leurs réponses.
Il ne suffit pas de demander et d’obtenir la ressource, mais nous devrions pouvoir spécifier
au serveur quelles transformations devraient s'appliquer, selon nos besoins.
De nos jours, nous utilisons tellement les API en réseau que nous devons essentiellement
fournir un ORM au client via HTTP (ou tout autre protocole).

Nous fournissons ici une liste de fonctionnalités (nous les appelons capacités) qui, selon nous, devraient être intégrées à une API réseau moderne,
en 2017.

6.1.1. Champs clairsemés (collection / ressource)

Le client doit pouvoir demander et obtenir des attributs spécifiques (c'est-à-dire un sous-ensemble) de la représentation des ressources.
Également liée, il convient de noter qu’une représentation d’une ressource pourrait avoir un ensemble de
attributs pour différents clients, généralement en fonction des autorisations du client ou du rôle utilisateur qu’il représente.

6.1.2. Associations à la demande (collection / ressource)

Le client doit pouvoir demander aux associations associées à la ressource initiale principale, dans la même demande.

Ce qui différencie une association d’un attribut est que le premier a
une identification dédiée. De plus, si l’API expose l’association en tant que ressource dédiée,
l'identifiant peut être utilisé comme identification.

Le client doit pouvoir trier en fonction d'un ou de plusieurs attributs et paginer la collection.
basé sur la page, la taille de la page et éventuellement un décalage.

6.1.4. Filtrage des collections (collection uniquement)

Le client doit pouvoir exécuter n’importe quel type de filtrage de collection, tant qu’il ne pose pas
toute menace à la sécurité ou ralentit les performances de l'API.

6.1.5. Requêtes d'agrégation (collection uniquement)

Le client doit pouvoir exécuter n’importe quelle requête d’agrégation, tant que cela ne pose pas
toute menace à la sécurité ou ralentit les performances de l'API.

6.1.6. Types de données !

Le client doit connaître les types de données des attributs de la représentation demandée d'une ressource.
Les formats de message fournissent certains types de données mais ils sont plutôt basiques.
Par exemple, JSON définit Chaîne, Booléen, Nombre, Tableau, et nul.
Rien de plus que cela, nous devons le définir dans les documentations.

Nous pensons que ces 5 types de données fournis par JSON ne sont qu’une blague pour les API modernes et que nous devrions
avoir une liste beaucoup plus grande d'options à sélectionner.
En outre, nous devrions pouvoir fournir des types personnalisés de manière simple. Par exemple, un champ est Chaîne mais
a une longueur maximale de 255 caractères, il suit une expression rationnelle spécifique, etc.

6.1.7 Twist complot: cette liste est sans fin

Bien que nous sentions que aujourd'hui ces capacités devraient exister dans toute API moderne, Cette liste n'est pas exclusive.
En fait, il pourrait y avoir des capacités à l’avenir qui pourraient ne pas sembler nécessaires aujourd’hui.
Par exemple, joignant une ou plusieurs ressources, d’autres opérations inspirées de la base de données appliquées aux ressources,
internationalisation et localisation des données, serveur HTTP / 2 Push sur certaines requêtes, livraison d'événements génériques à l'aide de HTTP Push sur d'autres
ressources sur des états spécifiques et d’autres capacités que nous n’avons même pas imaginées.
Dans tout les cas, ces capacités doivent être transparentes et auto-descriptives pour le client, sans aucune documentation ni implication humaine, autre
que de programmer le client pour prendre en charge le ou les types de média et de le pointer vers l’URI d’API initial.

6.2. Types de média vs HATEOAS

Maintenant, le lecteur pourrait se demander: où est le lieu approprié pour décrire ces capacités,
dans le type de support de notre API ou en utilisant HATEOAS?
Qu'est-ce qui va où?

6.2.1. Définir un nouveau type de support n'est pas facile et doit être évité

La création d'un nouveau type de support pour notre API est généralement considérée comme une mauvaise pratique.
Créez un nouveau type de support uniquement si vous êtes certain qu'aucun des éléments déjà publiés
Les types de supports peuvent s'intégrer dans la conception de votre API.

En outre, étendre un type de média existant ou ajouter un type de média complémentaire à un
existant (comme application / vnd.api + json + my_custom_data_types) ne fonctionnerait pas.
Non seulement la spécification de type de support existante ne fournit aucun principe d'extensibilité,
mais aussi, la raison principale est que le client doit comprendre le type de média avant de commencer.
Par conséquent, si nous souhaitons utiliser certains Nouveau types personnalisés dans notre API (déjà déployée), nous devrions publier
le type de média avant la main et laissez humains implémenter du code pour analyser complètement les réponses de l'API qui
suivez ce type de support ou les réponses de l'API que leur type de support inclut également ce nouveau type de support.

6.2.2. HATEOAS peut devenir assez lourd

Imaginez si nous devons décrire dans une ressource toutes les actions disponibles ainsi que l'API disponible
capacités dans cette ressource spécifique.
La réponse de notre API exploserait simplement en termes de taille tout en rendant notre API super complexe.

6.2.3. Équilibrage entre types de média et HATEOAS

L’idée est que les types de média décrivent les capacités génériques alors que HATEOAS
décrire les capacités spécifiques aux ressources.

Cependant, nous devrions noter que Les types de média ne sont pas analysés par le client (il n'y a jamais eu une telle intention de toute façon)
ce qui signifie que le client doit être préalablement programmé par un humain afin de prendre en charge ce type de support.
Par conséquent, le type de support ne peut pas être très restrictif, car cela limiterait la liberté du concepteur d’API.
pour concevoir l’API comme elle le souhaite.

Par exemple, pour la pagination, la plupart des API RESTy utilisent un page et un par page paramètre dans l'URL.
Si le type de support décrit comment paginer en utilisant, par exemple, un modèle d’URL sur le chemin de la ressource (comme / ressource? page = page & per_page = per_page & offset = offset)
cela voudrait dire que tout Les API qui suivent ce type de support doivent avoir la pagination qui suit ce modèle d’URL.
Le niveau de restriction devient plus évident lors de la description de capacités plus complexes.

En revanche, si tout le monde suit ce type de média, il est plus facile de programmer nos clients.
Plus précisément, en particulier lorsque le type de support est restrictif, si nous créons un client qui analyse les réponses à l’aide de ce type de support.
Ensuite, il est facile de la "configurer" pour une autre API qui suit également le même type de média.

HATEOAS devrait essentiellement complément type de média en fournissant la sémantique définie par le type de média hypermédia dans runtime
pour que le client fonctionne correctement.
Par exemple, HATEOAS pourrait décrire, ressource par ressource, si la pagination est prise en charge, quel est le maximum par page etc.

6.2.4. Une architecture alternative

Nous pensons que la spécification et l'utilisation actuelles du type de média sont obsolètes.
Si le génie logiciel nous a appris quelque chose, c'est que la composition peut appliquer le principe de responsabilité unique, si elle est utilisée correctement.
Inspirés par cela, nous proposerons ultérieurement un nouveau concept: les MicroTypes, de petits modules réutilisables combinés ensemble, peuvent former un type de support.
En conséquence, les clients devraient pouvoir même négocier des parties du type de support et non le type de support dans son ensemble.

De plus, au lieu de mélanger les données avec HATEOAS dans les réponses de l'API, nous introduirons l'introspection de nos ressources.

7. Spécifications de l'API aujourd'hui

Maintenant que nous avons défini ce que REST est, selon Roy, quelles fonctionnalités les API modernes devraient prendre en charge et où
ils devraient leur fournir,
voyons les spécifications des API REST (y) disponibles à compter d’aujourd’hui, septembre 2017, ce qu’elles fournissent et comment
ceux-ci suivent de près le modèle REST.

7.1. Notre cas d'utilisation

Notre cas d'utilisation est une miniature d'une autre application sociale.
Plus précisément, dans notre domaine API, nous avons un Utilisateur ressource qui a d'autres ressources associées, comme Micropost, Comme, etc

Pour notre format de message, nous utiliserons JSON car c’est le plus populaire, mais il pourrait s’agir de XML, YAML, etc.

  • Utilisateur
    • identifiant, une chaîne, jamais vide ou NULL, ID principal de la ressource
    • email, une chaîne, jamais vide ou NULL, avec une longueur maximale de 255 caractères, format de courrier électronique
    • prénom, une chaîne, avec une longueur maximale de 150 caractères
    • date de naissance, une chaîne représentant une date en fonction de iso8601, dans 2017-04-01 format.
    • créé à, une chaîne, jamais vide ou NULL, représentant un DateTime selon iso8601, en UTC
    • microposts_count un nombre entier

Donc, étant donné ces propriétés de modèle REST nous pourrait avoir les itinéraires suivants:

  • Utilisateurs Ressource (/ api / utilisateurs):
    • Liste des utilisateurs (GET / api / utilisateurs): Obtient une collection de Utilisateur Ressources
    • Créer un nouvel utilisateur (POST / api / utilisateurs): Crée un nouveau Utilisateur avec les attributs spécifiés.
  • Utilisateur Ressource (/ api / utilisateurs / id):
    • Obtenir un utilisateur (GET / api / users / id): Obtient les attributs du spécifié Utilisateur
    • Mettre à jour un utilisateur PATCH / api / utilisateurs / id: Met à jour un Utilisateur avec les attributs spécifiés
    • Supprimer un utilisateur DELETE / api / users / id: Supprime un Utilisateur

Utilisateurs et Utilisateur sont deux ressources distinctes qui sont souvent, à tort, considérées comme une seule et même ressource.
En outre, le fait que Utilisateurs est une collection de Utilisateur les objets, c’est parce que cela répond à nos besoins, mais il n’a pas nécessairement
être comme ça.

Comme nous l'avons mentionné, Utilisateur la ressource a aussi des associations (ou des relations / relations si vous préférez),
comme Micropostes.

7.1.1. Ressource utilisateur

En langage JSON simple, la ressource utilisateur devrait ressembler à ceci:


  "utilisateur": 
    "id":"685",
    "email":"[email protected]",
    "prénom":"Filippos Vasilakis",
    "date de naissance": "1988-12-12",
    "créé à": "2014-01-06T20: 46: 55Z",
    "microposts_count":50
  

7.1.2. Ressource utilisateurs (une collection de ressources utilisateur)

Une collection de Utilisateur les ressources, les Utilisateurs ressource, ressemblerait à:

{
  "utilisateurs": [{[{[[
    "id":"685",
    "email":"[email protected]",
    "prénom":"Filippos Vasilakis",
    "date de naissance": "1988-12-12",
    "créé à": "2014-01-06T20: 46: 55Z",
    "microposts_count":50
  , 
    "id":"9124",
    "email": "[email protected]",
    "prénom": "Robert Clarsson",
    "date de naissance": "1940-11-10",
    "créé à": "2016-10-06T16: 01: 24Z",
    "microposts-count": 17,
  ]

Maintenant que nous avons défini la portée de notre petite API, voyons comment cela serait implémenté.
dans les spécifications pour les API REST (y) actuellement disponibles. Nous pensons que la plupart des API actuellement déployées
ont beaucoup de similitudes avec les spécifications suivantes, à savoir la structure et la partie HATEOAS (en ce qui concerne
liaison), et par conséquent en comparant ces spécifications avec notre modèle serait suffisant.

Nous évaluerons les spécifications pour ce qui suit:

  • si elles suivent le modèle REST de Roy
  • si leurs messages sont ne pas auto-descriptif, autrement dit, prendre en charge le type de média de l’API dans notre client
    nous devons également lire et comprendre la documentation pour développer notre client
  • si elles requièrent un facteur humain multiplicatif pendant que l'API évolue

7.2. JSONAPI

JSONAPI a été créé à l'origine par Yehuda Katz, dans le cadre de la bibliothèque de données des braises d'Ember.
Depuis lors, beaucoup de gens ont contribué et sont devenus l’un des pays les plus soutenus
Spécifications API à partir de 2017 en termes d'outils et de bibliothèques.

7.2.1. Ressource utilisateur

{
  "Les données": 
    "id":"1",
    "type":"utilisateurs",
    "les attributs": 
      "email":"[email protected]",
      "prénom":"Filippos Vasilakis",
      "date de naissance":"1988-12-12",
      "créé à":"2014-01-06T20: 46: 55Z",
      "microposts-count":50
    ,
    "des relations": 
      "micropostes": 
        "liens": 
          "en relation":"/ api / microposts? user_id = 1"
        
      ,
      "aime": 
        "liens": 
          "en relation":"/ api / likes? user_id = 1"
        
      
    
  
}

7.2.2. Ressource utilisateurs (une collection de ressources utilisateur)

{
  "Les données":[[[[
    
      "id":"1",
      "type":"utilisateurs",
      "les attributs": 
        "email":"[email protected]",
        "prénom":"Filippos Vasilakis",
        "date de naissance":"1988-12-12",
        "créé à":"2014-01-06T20: 46: 55Z",
        "microposts-count":50
      ,
      "des relations": 
        "micropostes": 
          "liens": 
            "en relation":"/ api / microposts? user_id = 1"
          
        ,
        "aime": 
          "liens": 
            "en relation":"/ api / likes? user_id = 1"
          
        
      
    ,
    
      "id":"9124",
      "type":"utilisateurs",
      "les attributs": 
        "email":"[email protected]",
        "prénom":"Robert Clarsson",
        "date de naissance":"1940-11-10",
        "créé à":"2016-10-06T16: 01: 24Z",
        "microposts-count":17
      ,
      "des relations": 
        "micropostes": 
          "liens": 
            "en relation":"/ api / microposts? user_id = 9124"
          
        ,
        "aime": 
          "liens": 
            "en relation":"/ api / likes? user_id = 9124"
          
        
      
    
  ],
  "liens": 
    "soi":"/ api / users? page = 1 & per_page = 10",
    "suivant":"/ api / users? page = 2 & per_page = 10",
    "dernier":"/ api / users? page = 3 & per_page = 10"
  
}

7.2.3. Des reflets

Bien que la spécification fasse de gros efforts pour décrire la structure du document, nous voyons quelques
problèmes notables. À savoir:

  • Liens limités (pas de modèles d'URI, le client est considéré comme idiot)
  • Aucune action
  • Aucune information sur les attributs disponibles
  • Aucune information sur les types de données
  • Aucune description d'attributs

To sum up, it doesn’t entirely follow REST model while it requires both
documentation and multi-fold human factor.

7.3. HAL

HAL was created by Mike Kelly in 2012.
The key feature of HAL when it was released was the browsability/explorability of any API that adopted.
Another feature is the idea of curies, links inside the resource that lead to the documentation, targeted to
humans and not machines.

The resources of our use case that are presented here use JSON as a message format, but HAL is not tied to that.

7.3.1. User resource


    "_links": 
        "self": 
            "href": "/api/users/id"
        ,
        "microposts": 
            "href": "/api/microposts/user_id=id",
            "templated": vrai
        ,
        "likes": 
            "href": "/api/likes/user_id=id",
            "templated": vrai
        
    ,
    "id": "1",
    "name": "Filippos Vasilakis",
    "email": "[email protected]",
    "createdAt": "2014-01-06T20:46:55Z",
    "micropostsCount": 50,

7.3.2. Users resource (a collection of User resources)

{
   "_links":
      "self":
         "href":"/api/users"
      ,
      "curries":[[[[
         
            "name":"ea",
            "href":"https://example.com/docs/rels/rel",
            "templated":vrai
         
      ]
   ,
   "_embedded":{
      "users":[[[[
         
            "_links":
              "self":
                "href":"/api/users/id",
                "templated": vrai
              ,
              "microposts":
                "href":"/api/microposts?user_id=id",
                "templated": vrai
              ,
              "likes": 
                "href": "/api/likes/user_id=id",
                "templated": vrai
              
            ,
            "id": 9123,
            "name": "Filippos Vasilakis",
            "email": "[email protected]",
            "createdAt": "2014-01-06T20:46:55Z",
            "micropostsCount": 50
         , 
            "_links":
              "self":
                "href":"/api/users/id",
                "templated": vrai
              ,
              "microposts":
                "href":"/api/microposts?user_id=id",
                "templated": vrai
              ,
              "likes": 
                "href": "/api/likes/user_id=id",
                "templated": vrai
              
            ,
            "id": 9123,
            "name": "Robert Clarsson",
            "email": "[email protected]",
            "created-at": "2016-10-06T16:01:24Z",
            "microposts-count": 50,
         
      ]
   }
}

7.3.3. Reflections

Although this spec does have templated links, we see some notable issues. Namely:

  • No actions (they are supported by an unofficial extension)
  • No info on available attributes
  • No info on data types
  • No attributes description, requires documentation (however it does provide a link to documentation, through curries)

To sum up, it doesn’t entirely follow REST while it requires documentation and multi-fold human factor (curies facilitate that).

7.4. Sirène

Siren was created by Kevin Swiber in 2012 and revolves around entités, a URI-addressable resource that has properties and actions associated with it.

The resources of our use case that are presented here use JSON as a message format, but Siren is not tied to that.

7.4.1. User resource


  "class": [[[[ "user" ],
  "Propriétés": 
    "name": "Filippos Vasilakis",
    "email": "[email protected]",
    "createdAt": "2014-01-06T20:46:55Z",
    "micropostsCount": 50,
  ,
  "actions": [[[[
    
      "name": "get-user",
      "Titre": "Get User",
      "method": "OBTENIR",
      "href": "https://example.com/api/users/1",
      "type": "application/json",
    ,
    
      "name": "update-user",
      "Titre": "Update User",
      "method": "PUT",
      "href": "https://example.com/api/users/1",
      "type": "application/json",
      "fields": [[[[
         "name": "name", "type": "text" ,
      ]
    ,
    
      "name": "delete-user",
      "Titre": "Get User",
      "method": "DELETE",
      "href": "https://example.com/api/users/1",
      "type": "application/json",
    
  ],
  "links":[[[[
     "rel":[[[["self"], "href":"https://example.com/api/users/1" ,
     "rel":[[[["microposts"], "href":"/api/microposts?user_id=1" 
     "rel":[[[["likes"], "href":"/api/likes?user_id=1" 
  ]

7.4.2. Users resource (a collection of User resources)


  "class":[[[["users"],
  "Propriétés":nul,
  "entities":[[[[
    
      "class":[[[["user"],
      "rel":[[[["https://example.com/users/1"],
      "href":"https://example.com/users/1",
      "Propriétés":
        "name": "Filippos Vasilakis",
        "email": "[email protected]",
        "createdAt": "2014-01-06T20:46:55Z",
        "micropostsCount": 50,
      ,
      "links":[[[[
         "rel":[[[["self"], "href":"https://example.com/api/users/1" ,
         "rel":[[[["microposts"], "href":"/api/microposts?user_id=1" 
      ]
    ,
    
      "class":[[[["user"],
      "rel":[[[["https://example.com/users/9124"],
      "href":"https://example.com/users/9124",
      "Propriétés":
        "email": "[email protected]",
        "name": "Robert Clarsson",
        "birth_date": "1940-11-10",
        "created-at": "2016-10-06T16:01:24Z",
        "microposts-count": 17,
      ,
      "links":[[[[
         "rel":[[[["self"], "href":"https://example.com/api/users/9124" ,
         "rel":[[[["microposts"], "href":"https://example.com/api/microposts?user_id=9124" 
         "rel":[[[["likes"], "href":"https://example.com/api/likes?user_id=9124" 
      ]
    
  ],
  "actions":[[[[
    
      "name":"create-user",
      "Titre":"Create User",
      "method":"POST",
      "href":"https://example.com/users/",
      "type":"application/json",
      "fields": [[[[
         "name": "name", "type": "text" ,
         "name": "email", "type": "text" ,
         "name": "birth_date", "type": "date" ,
      ]
    
  ],
  "links":[[[[
    "rel":[[[["self"], "href":"https://example.com.api/users",
    "rel":[[[["next"], "href":"https://example.com.api/users?page=2"
  ]

7.4.3. Reflections

The spec takes a huge leap towards REST principles by supporting, links, actions with fields and data types, there are
still some issues that require human-involvement:

  • No custom types for the attributes of actions
  • No info on available and required attributes
  • No info on data types on response objects
  • Limited description for fields and resources

To sum up, Siren is very close to a self-described REST API but in practice it requires documentation and multi-fold human factor.

8. Ideal REST API

How many years these specs could sustain in terms of evolvability ? Are they built with a lifespan of 2-3 years or are they
built with a life span of 50 years?

8.1. Capabilities of an Ideal REST API

In an ideal REST API, the client should be able to have all the necessary information for both
the request and response.

  • About each resource returned from the API to the client:
    • default attributes and available (superset of default) attributes of the resource, based on the user’s permissions
    • data types for each attribute in the resource or any embedded association
    • Sorting/pagination, filtering and aggregation queries availability
    • data type of each attribute
    • default embedded associations and available associations to embed
      • recursively apply the same information for each association available for embedding
    • any other capability (HTTP/2 Server Push, event delivery etc)
  • About each resource sent to the API from the client
    • available actions on the resource
    • attributes, per action, the client can modify, based on the user’s permissions
    • required attributes of a resource (attributes a resource doit before sending it over)
    • data types of the attributes (could be different from the resource found in the response)
    • associations that are required or can be embedded to the initial request
      • recursively apply the same information for each association available for embedding

Bien que cette liste n'est pas exhaustive, an architecture style is timeless anyway,
we feel that the aforementioned capabilities ought to appear in an idealized modern REST API.

We should also note that the reason we don’t mention anything about the headers that are required, or, the status codes
is because we feel that these belong to the Protocol level and not in the Application level.
Any changes on this level imply that the API breaks the protocol.
However, we are pragmatic and we understand that an API designer could want to ajouter (not change)
a status code or a header in a given request/response and as a result, ideally, this should also be possible to be described.

8.1.1. Today’s REST is far from ideal

Now to the reader, it should be obvious that even if we manage to offload some of the aforementioned information
to the Media Type, we would still have a très complex, massive, response from the server that mostly includes HATEOAS
and not actual data
.

In our experience, such responses are very hard to implement correctly, test, be performant and even debug. Après tout,
a human will sit down and write the initial code and debugging the code by the eye is important.
Simplicity is crucial.

Moreover, some clients might not be interested in hypermedia and evolvability at all but only the data.
However such APIs force the clients to deal with it.

Ideally we would like to give the option to the client to decide the extend of the hypermedia that it
will support and follow, without taking on defaults. Some clients might want to follow 100% the HATEOAS part
of the API (and as a result be evolvable) some other clients might want the 50%, some clients might be interested
only in data.

By outputting a whole bunch of hypermedia-related information to the clients that, after all, might never use
them is a bad practice.

8.1.2. Making an API REST-compliant by downplaying its capabilities

One could argue that we require all APIs to support features that they shouldn’t, like resource manipulation.
For instance, we could have a weather API with application/vnd.weather+yaml Media Type
that is only supposed to provide a single attribute with its value, as Integer:

This API devrait be REST-compliant by not providing any API capabilities, hypermedia or actions.
Of course, the imaginary Media Type application/vnd.weather+yaml is supposed to provide all the necessary information
because otherwise the client would fail to understand things like

  • what are the attributes of the response
  • what is the data type of the temperature value (float, double, integer, bignum etc)

We feel that although this is true, most APIs are not as simple as that.
Moreover such APIs can’t actually be evolved without releasing a new Media Type and breaking the existing API clients.
There is no way of introducing change, which essentially breaks REST’s principles.

However we are pragmatic: we understand that such APIs will exist and API designers want to spend as less time as possible to build such APIs.
Introspected REST, an architecture that we will describe later, solves that by serving hypermedia
information on side and in an incremental way without breaking the simplicity.

8.1.3. A JSON API back in time

A JSON-based API built around 2006 would return just data. No hypermedia, no HATEOAS, only data.

In our use case, User resource would look like this:


  "user": 
    "id":"685",
    "email":"[email protected]",
    "name":"Filippos Vasilakis",
    "birth_date": "1988-12-12",
    "created_at": "2014-01-06T20:46:55Z",
    "microposts_count":50
  

As simple as that.

Compared with a HATEOAS-ed response it’s simple as hell, obvious, easy to debug and understand by a human (and a client).

Is it possible to build an API that is simple as that, be Hypermedia driven and give the client the option to decide
the level and type of HATEOAS it will follow?

8.2. Deriving the need for a new model

8.2.1. REST is complex

As we described earlier, mixing data with hypermedia leads to increased complexity, for both the server and the client.
Just compare the size of our resource’s data and the size of our resource when represented by
Siren, a hypermedia-ed API that doesn’t even being REST-compatible by missing numerous information as described
in its reflections.
Imagine how bloated the response would look like, if we added all the capabilities described in section 6.1.

Moreover, the hypermedia must be tailored for the user role the client acts on behalf of.
For instance, a user with very basic access role might only have access to retrieving resources and not manipulating them.
Or such role could only have access to specific capabilities of the API.
As a result, the hypermedia provided on the response object should reflect that by not providing hypermedia that will lead to unauthorized access.
In fact, such design is quite difficult to implement and test from the server side.

8.2.2. REST enforces possibly useless information

In REST, even if the hypermedia are rendered by taking into account the user’s role, eventually we might send more data that the client wants.
Exactly because we don’t know in advance what the client might need, we must send all the possible hypermedia information to the client, just in case.
The client however could only be interested in the data, or specific hypermedia types, like links, but instead gets a fully bloated response by the server.

8.2.3. REST sacrifices performance for evolvability

Complex or long-lived APIs tend to have many hypermedia data (links, actions, custom metadata)
related to the resource itself, its associations and related resources.
As a result, even if the actual data could be very small the resulted response object gets much larger in size slowing down the server rendering
and the client receiving and parsing.
The performance issues become more apparent on lossy networks like mobile clients, a trend that has increased over the past decade,
or on constrained devices and environments, like IoT.

8.2.4. REST does not support caching of hypermedia

In practice, the hypermedia part of a resource rarely changes, compared to the resource’s data.
In REST, by design, the client can’t cache the hypermedia part of the resource, even for relatively small amount of time, because
hypermedia is part of the resource, thus caching the hypermedia can’t be separate from caching the response itself.

8.2.5. REST doesn’t make it easy to evolve hypermedia

Another issue of REST is that due to the fact that everything is mixed in together, evolving hypermedia separately from the data
can’t happen
.
We understand that this is actually another feature of REST design and not an issue, treating a response object as a whole and not breaking into
different parts like hypermedia and data, however, in practice, this poses difficulties for easier evolvement and maintenance of the API.

Although REST is not dependent on any protocol or spec, the truth is that it has dominated in HTTP.
As we described earlier, in protocols like HTTP, content negotiation between client and server is achieved using Media Types,
which is the only mechanism to define the semantics of a response.
Given that composite Media Types never had real composability, and the fact that they cannot be parsed by clients,
there is a trade off between what should go to the Media Type and what to the actual response through
hypermedia, as described in section 6.2.3.
This limits the design flexibility and evolvability.
As a result Media Types become big monoliths that are inflexible and limit the evolvability of the API.

8.2.6.1. No backwards compatible with any RESTly or RESTless API

In a perfect world, APIs are built to be alive for many decades and clients are exploiting every little feature of the API and its Media Type.
However, we are in a pragmatic world where nothing is perfect and clients are built by humans who take decisions based on their time and money.

Although we firmly believe that a REST API is better than any RESTly or RESTless API, we understand that there could be cases where API designers
devoir initially skip hypermedia part.

The problem is that when REST is applied to HTTP, it doesn’t allow us to easily integrate hypermedia at a later point.
The reason is that, in a RESTless API, adding hypermedia at a later stage would mean that we would need a new Media Type because
otherwise it would break the current semantics
.

We would like to see a model that embraces both architectural API styles:

  • APIs that are built to last decades and thus, support full hypermedia from the very first day of their release
  • APIs that don’t have hypermedia (the reason is none of our business), yet it is required to add hypermedia, later, in an incremental way without
    breaking existing clients or limiting API’s flexibility
8.2.7.2 REST does not embrace composition

Although REST does not rejects the idea of composability of different API capabilities using different specs in the same response, or composite Media Types,
it doesn’t embrace it either.
The symptom of non-composability is clearly visible in protocols like HTTP where Media Types
act as big monoliths trying to describe everything in one place.
RFC 6906 (The ‘profile’ Link Relation Type) was created to overcome such issues
but as we will see later this specifications lags behind over true composability and
proper negotiation of the different profile types from the client perspective.

In Introspected REST, the MicroTypes is a conceptual solution to the outdated Media Type concept
and allows us to mix-in different concepts for different kind of metadata of a resource,
yet have all of them on demand and separated by the actual data.

9. Introspected REST

Simple things should be simple and complex things should be possible.

— Alan Kay

In the following section we will describe our new architectural style based on a model for Networked APIs that goes beyond REST.
The model itself steps on Roy’s initial REST model but with the difference that instead of providing resource hypermedia at
runtime, it provides them on the side, only if requested.
Hence, by keeping the interface uniforme the derived 3 out of 4 REST constraints that Roy defined still exist in this model:
identification of resources; manipulation of resources through representations et self-descriptive messages.
However, instead of having the constraint of hypermédia en tant que moteur de l'état de l'application (HATEOAS), we have
introspection as the engine of application state (IATEOAS).
Moreover, the introspection process can provide other kind of information, apart from hypermedia and links, that
can facilitate the client to take decisions on how to proceed with the application’s requests.

To achieve this, composition of different specs is a vital part of our model and for that we will use a new concept,
MicroTypes, small reusable modules that a final Media Type can be composed of.
Before moving on, we will give concise definitions over hypermedia and metadata and break it down to different kinds of classes,
according to Introspected REST model.
These terms can overlap in the REST, however we feel that each one has its own place in our model that embraces composability.

9.1. Data, metadata and hypermedia

9.1.1. Les données

Data is the main variables of a resource, at a given state, at a given time.
Data is very volatile compared to other parts of a response.

9.1.2. Hypermedia

Originally the hypermedia term was mostly used for linked data, in the sense of hyperlinks.
In REST, eventually, it also includes information for interaction and resource manipulation.
Hypermedia can be dynamic or static but regardless they are not considered part of the response data, because they define
ways to interact with the data
.

Hypermedia is a very broad term and needs to be broken down in different parts.
Although there isn’t any clear definition or consensus in the literature and the community, we will try to provide definitions and descriptions for
all the different types of Hypermedia, according to our model’s perception.

The most basic class of hypermedia, basically URIs that can be used to provide linking between related resources to the primary resource.
The properties of a link, like placement inside the response, strictly follow the semantics of the Media Type agreed.

9.1.2.2. actes

Actions are links along with information for manipulating a resource.
Although CRUD are the most popular actions of a resource, the beauty with REST, and consequently with Introspected REST,
is that actions can go beyond plain CRUD.
In fact, we can define any type of action or meta-action of our internal resource, through the representation that we expose.
As a result, actions of a resource could be quite complex or simplistic depending on the needs and decisions of the API designer.
Actions should also describe any relevant information for the client to perform it, unless the Media Type itself describe those details.

9.1.2.3. Forms

Another way of describing the manipulation options of a resource is the notion of forms.
The difference between actions and forms is that the latter are strictly semantically equivalent to an HTML form,
for the client to render.

9.1.3. Metadata

If hypermedia is links and actions, then what is metadata ?

Metadata are information about the resource that is not related with the data, including hypermedia.
In essence, metadata is a superset of hypermedia and it’s crucial for the client
to understand API’s responses, access the API’s capabilities and manipulate the resources.

Metadata could be API-specific, resource-specific, action-specific or even object-specific.
There could also be different kinds of metadata: runtime (i.e. pagination information), structural (i.e. data types of a resource object),
hypermedia (i.e. links, actions, forms), informational targeted to humans (i.e. general information, descriptions), etc.

Usually metadata is much less volatile than data, if not static, except runtime metadata
that depend on the request and the resource at the given time and state respectively.

9.2. MicroTypes: reusable modules composing a Media Type

Imagine how poor the Web would have been if we had limited HTML to what was
needed by an FTP client. That’s what most JSON APIs are today.

— Roy Fielding, on Gazouillement, 27 Aug 2016

We have been talking so much about the concept of MicroTypes but what exactly are ?

Currently, Media Types act as big monoliths that clients need to understand beforehand through human involvement.
We believe that Media Types should be broken in smaller
reusable media types, MicroTypes, each describing very carefully a specific functionality of a modern API.
The reasoning is that, in our experience, we have seen that different APIs and API specs define the same functionalities in similar,
but not identical, ways.

Examples of MicroTypes could be semantics for:

  • pagination
  • querying over url (applying filters, aggregations, pagination/sorting on a resource),
  • resource/association inclusion in the same response
  • semantic/linked data
  • hypermedia actions (required fields, available fields),
  • data types and resource schemas
  • information d'erreur
  • and more advanced, like HTTP/2 server push for specific resources/states etc

Each one of these could be defined as separate MicroTypes that specify in isolation how that part of the API works.
At the same time they should be generic enough or follow some specific semantics so that it’s possible to be referenced parent
Media Types targeted for Introspected APIs.
The parent Media Type doesn’t need to know in advance all the MicroTypes that the API designer intends to use
because that would mean that adding new MicroTypes would require a new parent Media Type which consequently means breaking the clients.
Instead, each MicroType should be attachable to a parent Media Type that defines introspected behavior and clients
would take into account only MicroTypes that are programmed to understand.

9.2.1. Benefits of MicroTypes

The benefits when leveraging such architecture are multi-fold.

9.2.1.1. Granular parameterization of API functionality by clients

First, by allowing the client and server to do the regular negotiation flow even for those sub-media-types, the communication
between the 2 ends is parameterized to the needs of the client, down to the semantics level.
For instance, a server might provide 3 MicroTypes for error information, each one having different representation or semantics.
By letting the server to decide the appropriate MicroType for the client by analyzing the client’s incoming request,
might not be efficient as the client can only send a part of its properties through the request, for various reasons like privacy concerns and performance,
and thus the server has partial knowledge of the client’s state and properties.
The server has to make an arbitrary choice for the client, what it thinks it’s thinks best, using this partial knowledge.

Instead, by giving the client the option to negotiate parts of the API functionality, we shift the responsibility towards the client
to select the best representation and semantics of various, isolated, API functionalities.
Given that the client can know much more about its needs than the server, it will make the best available choice
for each API functionality, from the server’s options, which eventually will lead to the optimized combination of
MicroTypes.
As we will see later, in HTTP protocol, this is called reactive negotiation, a forgotten but still valid negotiation mechanism.

9.2.1.2. Reusability

Secondly, the MicroTypes specs and possibly implementations can be re-used by both the servers and clients.
Instead of defining a whole Media Type, API designers will be able to include various small modules
that extend the API functionality they way it’s needed.
We firmly believe that once the community defines a number of MicroTypes, it will be much easier for an API designer
to design a new API by reusing the MicroTypes she thinks fit best to her needs.

9.2.2. MicroType shims

Imagine that we want to use an existing spec as a MicroType, like JSON Schema.
We cannot create a MicroType out of it with just a reference
to the original spec because it lacks the context of the underlying protocol (like HTTP) and Media Type with which it will be
utilisé.
It also lacks information about the requirements of the parent Media Type and the compatibility with other MicroTypes.
Instead, we need to extend the original spec with the necessary, additional, semantics in the context
of Media Types.
Those semantics should be as minimal as possible, with respect to the initial specification and without altering its core semantics
but enough for usage in its new context.
When this method is followed, the new MicroType is called a “wrapper” or a “shim” of the original spec.

9.3. Introspection as the engine of application state (IATEOAS)

The idea of introspection is to be able to examine properties of a system at runtime.
In the case of Introspected REST, introspection defines a process for a client to be able to introspect
the API’s, resource’s, action’s or even object’s metadata at runtime
.
Through those metadata, server provides all the available states, manipulation actions as well as the available transitions.
The implementation of the process is up to the API designer although usually a RESTish interface even for each MicroType’s metadata is a wise choice.
In any case, we would like to point out some key properties that should appear on any introspection process.

9.3.1. Composability over monoliths

The process should embrace the use of distinct MicroTypes to form a Media Type instead of using a single Media Type.
Such an architecture will lead to a system whose each MicroType’s metadata is independent, self-contained and detached from the metadata
of the rest MicroTypes.

The API designer should premier investigate and embrace the use of MicroTypes, RFCs and specs that are already defined and published, instead of
creating her own custom, unpublished spec.
The reason for this suggestion is that creating a new spec is difficult and usually such custom specs are used only for domain-specific APIs that
were created for and live as long as this API is used.
Instead, by trying to adapt published, battle-tested, RFC-community-reviewed specs assures the API designer in terms of compatibility,
adoptability, clarity
and possibly implementation, for both ends of the communication.

9.3.2. Plain data separated from metadata

The process of introspection should be distinctly different from requesting data.
To that extend, introspection responses should not include any data but only metadata, and data
responses should not include any metadata, except, possibly, runtime metadata.

9.3.3. Identifiable metadata of each Microtype

Given that metadata are already separated from plain data, by being able to identify and retrieve metadata
of a specific MicroType there are various advantages because each MicroType becomes independent and self-sufficient.
Par exemple, mise en cache will be possible using the underlying protocol’s mechanisms, for each metadata type separately.
Another example is the detached evolvability of each MicroType’s metadata, independently, given that the MicroType’s semantics permit that.

9.3.4. Discovery of resource capabilities

An Introspected REST API devrait fournir capabilities discovery per resource that provides
all the necessary information to the client to understand what it is possible to request from the API.

9.3.5. Client bootstraping

An Introspected REST API devrait fournir un API-wide capabilities discovery that lists all MicroTypes that are used API-wide along
with resources that can be accessed directly and in general, any information that could be of interest and could help the client
to bootstrap faster.

The location of this detailed list should be in the conceptual racine resource/URL of the API.

9.3.6. Automatic documentation generation

Possibly the API will provide a MicroType targeted to humans and not machines that contains informational descriptions and explanations.
Il convient de noter que this information must not be needed for a client to parse and understand the API responses,
and even for humans such information should weight very little compared to the rest metadata.

In the same way, the API should automate the generation of the documentation using all metadata from all MicroTypes for every resource.
The way the documentation is requested and its format should be distinctly defined by a MicroType or the parent Media Type.

10. Introspected REST applied to HTTP

Introspected REST architectural style is not bound to any protocol or spec, just as is REST.
Here we will review the challenges that are rising through its adaptation in HTTP protocol.
For instance, we need to solve issues like announcement and negotiation of MicroTypes bound to a Media Type,
priority order in case of overlaps or collisions, identification, and
the actual introspection process in HTTP.

10.1 Revisiting content negotiation in HTTP

As we have already seen, content negotiation in HTTP is achieved through Acceptez request header but it’s not the
only header which can be used by the server to determine the appropriate representation for the client.
Accept-Charset, Accepter l'encodage, Accept-Language request headers can also be used.
In practice, User-Agent header is also used by the server for choosing the right content for the client
because it contains some device and agent characteristics, although it’s not part of the standard negotiation headers.
Lately even, a new draft standard is being created, HTTP Client Hints,
that extends the HTTP with new request headers which indicate device and agent characteristics.
The server uses all those headers as hints in order to determine the most suitable representation of the content
to be served to the client.

This hint-based mechanism, which according to RFC 7231 is called server-driven
or proactive content negotiation, has been extensively used in HTTP protocol.
In the context of MicroTypes and Introspected REST, using this mechanism, the client
can negotiate for runtime MicroTypes: API functionalities that define semantics
for the data and runtime metadata.
This type of MicroTypes, should tend to appear less often because
if anything can be introspected on the side instead of runtime, it will be
defined as non-runtime, introspective metadata.

Interestingly, RFC 7231 notes that proactive negotiation has
some serious disadvantages:

Proactive negotiation has serious disadvantages:

o It is impossible for the server to accurately determine what might
     be “best” for any given user, since that would require complete
     knowledge of both the capabilities of the user agent and the
     intended use for the response (e.g., does the user want to view it
     on screen or print it on paper?);

o Having the user agent describe its capabilities in every request
     can be both very inefficient (given that only a small percentage
     of responses have multiple representations) and a potential risk
     to the user’s privacy;

o It complicates the implementation of an origin server and the
     algorithms for generating responses to a request; et,

o It limits the reusability of responses for shared caching.

— RFC 7231

In fact, from the beginnings of HTTP (since RFC 2068, published in 1997),
the protocol allowed another negotiation type: agent-driven or reactive content negotiation negotiation,
that matches very well our introspective concept.
As RFC 7231 notes, in reactive content negotiation the server provides a
list of options to the client to choose from.

With reactive negotiation (a.k.a., agent-driven negotiation),
  selection of the best response representation (regardless of the
  status code) is performed by the user agent after receiving an
  initial response from the origin server that contains a list of
  resources for alternative representations.

(…)

A server might choose not to send an initial representation, other
  than the list of alternatives, and thereby indicate that reactive
  negotiation by the user agent is preferred. Par exemple, le
  alternatives listed in responses with the 300 (Multiple Choices) and
  406 (Not Acceptable) status codes include information about the
  available representations so that the user or user agent can react by
  making a selection.

— RFC 7231

With reactive negotiation, the client is responsible for choosing the most appropriate representation,
according to its needs.
That goes inline with Introspective REST, as the client, after receiving all the possible server options,
uses the ones that best fit to its use case or understands better.
As the RFC notes, such negotiation has the advantage of choosing the best combination of MicroTypes,
because the client does the selection out of a predefined list that the server publishes.

10.2. Runtime MicroTypes

Runtime MicroTypes are targeted for API functionality that is used during the request/response cycle
of plain data.
Such functionality could be pagination, URI querying language, error descriptions etc or it could even be
semantics around the data itself.
It should also be noted that even runtime MicroTypes could have content for introspection but the key difference
from pure introspective MicroTypes is that part of their functionality affects the semantics of the client’s request
or server’s response.

The negotiation of runtime MicroTypes should follow the regular negotiation flow:
The client should negotiate for the principal Media Type using the Acceptez demande
header and the server responds with Type de contenu response header, denoting the selected representation.
However the key difference is that for each principal Media Type, it should also
negotiate for the MicroTypes to be used with it.
For that, we will employ the Media Type parameters, a rarely used mechanism:

Media types MAY elect to use one or more media type parameters, or
  some parameters may be automatically made available to the media type
  by virtue of being a subtype of a content type that defines a set of
  parameters applicable to any of its subtypes. In either case, the
  names, values, and meanings of any parameters MUST be fully specified
  when a media type is registered in the standards tree, and SHOULD be
  specified as completely as possible when media types are registered
  in the vendor or personal trees.

Parameter names have the syntax as media type names and values:

        parameter-name = restricted-name

— RFC 6838

An example of an imaginary Media Type with a couple of parameters for MicroTypes is:

Accept: application/vnd.api+json; pagination=simple-spec; querying=graphql;

In the aforementioned example, the client asks for representation of application/vnd.api+json,
(which as we have seen earlier it vaguely means a vendor application that follows the semantics of api, in JSON representation)
but wants the pagination to follow the semantics of simple-spec and the querying language of graphql.

The client should be able to even set a preference order:

Accept: application/vnd.api+json; pagination=simple-spec; querying=graphql; querying=jsonapi;

Here the client shows preference to the imaginary graphql querying language but if that doesn’t exist
then it will accept the jsonapi querying language.
It should be noted that this preference is different from a Media Type preference using the relative
poids q parameter (also called quality value) as it applies to the MicroType level.
An example with multiple Media Types could be:

Accept: application/vnd.api+json; pagination=simple-spec; querying=graphql; querying=jsonapi, application/vnd.api2+json; pagination=simple-spec; querying=jsonapi; querying=jsonapi; q=0.9

In this example the client shows preference to the application/vnd.api+json Media Type (it has default quality value of 1.0)
with specific preferences on MicroType level, as we explained above.
However if this Media Type is not available then it will accept the next most preferred, application/vnd.api2+json, by requesting
specific MicroTypes.

If the server can provide only the less preferred Media Type with the less preferred querying it would answer:

Content-Type: application/vnd.api2+json; pagination=simple-spec; querying=graphql

10.3. Introspective MicroTypes

Introspective MicroTypes don’t alter the semantics of request/response cycle but are still valuable to the client
and the decisions they should take based on the current state and the input from the application developer.
They can provide information about the data types, RDF Schema of the resources, etc.
Introspective MicroTypes should employ reactive negotiation.

The question though is how can the server advertise the availability of MicroTypes for the client
to introspect.

Ideally we would like to inform the client for all possible options through HTTP instead of employing a serialization format.
Unfortunately, the HTTP protocol doesn’t say much about this type of negotiation, only that the status code when requesting
such information should be 300 and Lien relation header of RFC 5988 could be potentially used
to provide the list with all the available options,
mostly for historical reasons that date back to RFC 2068:

The 300 (Multiple Choices) status code indicates that the target
  resource has more than one representation, each with its own more
  specific identifier, and information about the alternatives is being
  provided so that the user (or user agent) can select a preferred
  representation by redirecting its request to one or more of those
  identifiers. In other words, the server desires that the user agent
  engage in reactive negotiation to select the most appropriate
  representation(s) for its needs (Section 3.4). (…)

For request methods other than HEAD, the server SHOULD generate a
  payload in the 300 response containing a list of representation
  metadata and URI reference(s) from which the user or user agent can
  choose the one most preferred. (…)

Note: The original proposal for the 300 status code defined the
  URI header field as providing a list of alternative
  representations, such that it would be usable for 200, 300, and
  406 responses and be transferred in responses to the HEAD method.
  However, lack of deployment and disagreement over syntax led to
  both URI and Alternates (a subsequent proposal) being dropped from
  this specification. It is possible to communicate the list using
  a set of Link header fields [RFC5988], each with a relationship of
  “alternate”, though deployment is a chicken-and-egg problem.

— RFC 7231

To our knowledge, reactive negotiation has never been analyzed, used or suggested before.
Here, apart from Lien relation header, we also suggest two more alternative implementation to solve
this issue and we will let the community to choose what is the more appropriate solution.

10.4.1 The HTTP OPTIONS method

The server can describe the meta-data of a resource in the response body of the OPTIONS demande.
In fact, OPTIONS method has historically been used for getting information on methods supported on a specific resource.

According to RFC 7231 this method should be used to
determine the capabilities of the server for the targeted resource:

The OPTIONS method requests information about the communication
options available for the target resource, at either the origin
server or an intervening intermediary. This method allows a client
to determine the options and/or requirements associated with a
resource, or the capabilities of a server, without implying a
resource action.

— RFC 7231

The OPTIONS method could be used for the server to provide a list of available introspective MicroTypes
and let the client choose what it thinks best.

The same RFC mentions that there isn’t any practical use of sending an OPTIONS request
to the root url.

An OPTIONS request with an asterisk (“*”) as the request-target
(Section 5.3 of [RFC7230]) applies to the server in general rather
than to a specific resource. Since a server’s communication options
typically depend on the resource, the “*” request is only useful as a
“ping” or “no-op” type of method; it does nothing beyond allowing the
client to test the capabilities of the server. For example, this can
be used to test a proxy for HTTP/1.1 conformance (or lack thereof).

— RFC 7231

However, we feel that this is the perfect case for hosting an API’s discovery for available capabilities using
reactive negotiation.
We could keep the / * for “ping” or “no-op” type of method as the RFC notes and have the root
/ for listing all API’s capabilities through MicroTypes for all resources, as IATEOAS denotes.

Now that we know how to fetch the MicroTypes that the server offers, we need to find
an appropriate representation for it.
One option is to employ a common JSON format for describing each MicroType, its URL for introspection along
with the expected Media Type the response in the specified URL uses.
For instance if we would like to introspect resource /api/users/1 of an API we would get the following
information by sending an OPTIONS request to the resource’s url.


  "JSON-Schema": 
    "url": "/api/users/1?microtype=json-schema",
    "method": "OPTIONS",
    "content-type": "application/schema+json"
  ,
  "RDF": 
    "url": "/api/users/1?microtype=rdf",
    "method": "OPTIONS",
    "content-type": "application/rdf+xml"
  ,
  "JSON-LD": 
    "url": "api/users/1?microtype=json-ld",
    "method": "OPTIONS",
    "content-type": "application/ld+json"
  

The problem though is that such functionality (sending an OPTIONS demande à /api/users/1) must be described
somewhere so that the client knows where to look for it, possibly in the parent Media Type or using another MicroType.
An alternative option is to use the OPTIONS request in combination with the Lien header, as described later, that will announce
the MicroTypes availability. Such functionality should still be described somewhere as
RFC 7231 only makes a suggestion for the Lien header usage.

It is our intention to advice the community to use this solution for the introspection process, without the Lien entête
but with a response body that describes the MicroTypes availability.
The structure and semantics of the response could be available in various serializations and formats and the clients could
specify their preference using the regular, proactive, HTTP negotiation flow of Media Types.
Although, as we will see later, it comes at a cost, we feel that it’s the best among all three solutions presented here
and the conceptual notion of OPTIONS method, as described by HTTP specs, matches very well with our intended use case.
Furthermore, such process gives much more flexibility to append any additional information to the client, than
an HTTP header.

10.4.2. Well-known URIs and JSON Home

RFC 5785 defines a pre-defined URI for accessing server’s various metadata:

It is increasingly common for Web-based protocols to require the
  discovery of policy or other information about a host (“site-wide
  metadata”) before making a request. For example, the Robots
  Exclusion Protocol http://www.robotstxt.org/ specifies a way for
  automated processes to obtain permission to access resources;
  likewise, the Platform for Privacy Preferences [W3C.REC-P3P-20020416]
tells user-agents how to discover privacy policy beforehand. (…)

When this happens, it is common to designate a “well-known location”
  for such data, so that it can be easily located. However, this
  approach has the drawback of risking collisions, both with other such
  designated “well-known locations” and with pre-existing resources.

To address this, this memo defines a path prefix in HTTP(S) URIs for
  these “well-known locations”, “/.well-known/”. Future specifications
  that need to define a resource for such site-wide metadata can
  register their use to avoid collisions and minimise impingement upon
  sites’ URI space.

— RFC 5785

Using this specification, the server can register a well-known
URI that is expected to be the first URI the client requests to introspect.
To that extend, a new draft spec is being developed, JSON Home
that defines such document structure that provides all the server resources and capabilities.
Regardless if JSON Home is used, well-known URIs can provide a way to introspect only the
server-wide capabilities:

Ici, métadonnées would be a new well-known URI registry that either defined in the parent Media Type
or defined by itself as a MicroType.
The spec does not provide a scheme for well-known URIs per resource or nested URI and this means
that we need to build something upon well-known URIs functionality in order to provide
introspection per resource.
How this will be achieved can be defined by the community, if used eventually,
but a possible implementation could be to pass
the desired resource URL as a query in the métadonnées well-known URI registry:

/.well-known/metadata?query=/api/users/1

Again as with HTTP OPTIONS, the server will either have to provide a representation
of the available MicroTypes inside the response body of the well-known URI or use the Lien header.

Although this solution could work, we feel that RFC 5785
was not designed to be used for such specific URIs but instead for more generic properties
that usually apply to the host itself.

Regadless if HTTP OPTIONS or well-known URIs are used, Lien header, defined in RFC 5988,
is an alternative way of publishing the available MicroTypes by the server,
in a representation-agnostic way.

A means of indicating the relationships between resources on the Web,
  as well as indicating the type of those relationships, has been
  available for some time in HTML [W3C.REC-html401-19991224], and more
  recently in Atom [RFC4287]. These mechanisms, although conceptually
  similar, are separately specified. However, links between resources
  need not be format specific; it can be useful to have typed links
  that are independent of their serialisation, especially when a
  resource has representations in multiple formats.

To this end, this document defines a framework for typed links that
  isn’t specific to a particular serialisation or application. Cela fait
  so by redefining the link relation registry established by Atom to
  have a broader domain, and adding to it the relations that are
  defined by HTML.

— RFC 5988

As the next (draft) version of RFC 5988 notes:

a link published through Lien header can be viewed as a statement of the form
“link context has a link relation type resource at link target, which has target attributes”.

— rfc5988bis-07

As a result, this RFC provides us a representation-agnostic mechanism through which we can
announce link relations of the current visited URL, along with their relation types.
For instance, the following example

Link: ; rel="previous";
     title="previous chapter"

would denote that that “chapter2” is previous to this resource in a logical
navigation path.
Note that title is a target attribute or parameter to this link relation.

In the case of Introspected REST, we would use it to announce introspective MicroTypes related
to the resource the client visits.
By exploiting the target attributes we would also like to specify the HTTP method and
optionally the Media Type the client should expect in order to introspect
the given MicroType.

Link: ; rel="microtype";
     method="options"; type="application/schema+json" name="json-schema",
      ; rel="microtype";
     method="options"; type="application/schema+json" name="rdf",
      ; rel="microtype";
     method="options"; type="application/schema+json" name="json-ld",

Also related, Erik Wilde is working on an IETF draft, named Link Relation Types for Web Services
that defines a way to announce metadata of a resource through this mechanism.
Given that and also the fact that this solution has the advantage of solving the MicroTypes announcement
in the HTTP protocol without being tied to a specific serialization, it’s easy to think that it’s the
most appropriate way to specify the MicroTypes supported on a specific resource.

Unfortunately, this solution has a couple of drawbacks.
First and foremost, the link header size is limited and if other headers of the response
are already overloaded then the server might refuse to render the response to the client
but instead return an HTTP error possibly “413 Request Entity Too Large” or “414 Request-URI Too Long”
although there isn’t an HTTP status code explicitly defined for such case.
A possible solution to this could be Linkset: A Link Relation Type for Link Sets RFC proposal
(a work also by Erik Wilde) but currently it’s in draft state.
Once published, a Linkset could group together a set of links and provide them to the client by reference.
However Linksets don’t actually solve our issue because eventually the MicroTypes announcement would not
be solved in the HTTP level as a Linkset would have to provide a body format as well.

Another issue is that the server cannot specify a caching strategy for all links at once because there
is no mechanism in HTTP which allows us to specify caching directives for specific headers only.
As a result, unless we used a Linkset which we can’t yet and would cancel any advantages that Lien header provides
due to the need of a response body,
the client would have to dereference all MicroTypes to figure out their caching properties.

On a side note, over the past few years, we have seen an explosion of link types
used along with Lien header defined by RFC 5988.
The authors of Introspected REST are skeptical with this trend and feel that the Lien header should
not be overused.
For instance, having more than 5 links in the Lien header feels that something is wrong, probably too many things
are defined in the protocol level whereas maybe they should be defined somewhere else.
We will let the community to decide if this approach is good for publishing MicroTypes but we would like to stress
the point that having a link in the HTTP level through Lien header might be better
for related resources that all clients would understand
, which is not always the case in Introspected REST.
The API designer could add more MicroTypes, progressively, as the time passes and simultaneously,
some clients might not be interested or understand all MicroTypes of an Introspected REST.
Requiring the client to receive all MicroType information for every data request is made
would probably be against the principles of Introspected REST.

10.5. Considérations

10.5.1 Diversifing from existing RFCs

Although we have managed to apply Introspective REST to HTTP, a protocol that has been influenced so much
by Roy’s REST model (and
vice verca) this adaptation comes to a cost: we need to diversify from some RFCs specifications that we make use of.
Fortunately this diversification is relatively very small compared to the gains and all changes are
backwards compatible with existing deployed clients.

10.5.1. HTTP OPTIONS responses are not cacheable

First and most importantly, according to RFC 7231:

Responses to the OPTIONS method are not cacheable.

— RFC 7231

This is the biggest breaking change to existing HTTP specs that Introspected REST applies.
Unfortunately for a reason unknown to us, HTTP spec requires the clients to not cache responses of
HTTP OPTIONS, essentially breaking out thinking of detached hypermedia and other metadata from plain data.
In practice though, adding cache headers in that HTTP method should be possible although
limitations by existing client implementations could exist.
If an API designer doesn’t want to break this part of HTTP spec then she should define the introspection
process through the other suggested solutions, or come up with a new one.
What is important though is that, as Introspected REST specifies, introspection process should be recognizably distinct from regular
data requests.

The authors of Introspected REST don’t see the reasoning of this constraint by HTTP spec and advise the community to investigate
the possibility of ignoring this limitation and proceed with HTTP OPTIONS introspection
process that fits best to this architectural style.
Eventually, that would lead the IETF to completely drop it from HTTP spec.
Also, although the change itself could be considered as breaking because we alter a
functionality that RFC 7231 specifies,
this alteration does not break existing clients but only the existing spec, because
allowing clients to cache a response, which previously was not allowed, is backwards compatible.

10.5.2. Media Type parameters must be very well defined beforehand

According to RFC 6831 any Media Type parameters must be very well defined beforehand:

Media types MAY elect to use one or more media type parameters, or
  some parameters may be automatically made available to the media type
  by virtue of being a subtype of a content type that defines a set of
  parameters applicable to any of its subtypes. In either case, the
  names, values, and meanings of any parameters MUST be fully specified
  when a media type is registered in the standards tree, and SHOULD be
  specified as completely as possible when media types are registered
  in the vendor or personal trees.

This goes against our concept of arbitrary number of autonomous MicroTypes that can be included by a parent Media Type parameters.
However, we feel that given the sparse use of Media Types parameters, such breaking change will have a very small effect.
The authors of Introspected REST advice the community to investigate the possibility of pushing IETF to drop this requirement,
or extend Media Type parameters with specialized parameters that can have arbitrary names.

10.5.3. Media Types must function as actual media formats

Another thing that we differentiate is that according to same spec, each Media Type’s
primary functionality shoud be that of being media formats.

Media types MUST function as actual media formats. Registration of
 things that are better thought of as a transfer encoding, as a
 charset, or as a collection of separate entities of another type, is
 not allowed. For example, although applications exist to decode the
 base64 transfer encoding [RFC2045], base64 cannot be registered as a
 media type.

This requirement applies regardless of the registration tree
 involved.

RFC 6831

In our concept of MicroTypes, the parent Media Type acts as the base media format but has
the possibility to be extended by small components, MicroTypes.
These small components, which could be different in each request, define functionalities of different parts of the API
and such functionality is not always in the context of media formats as RFC 6831 indicates.

10.5.4. Mixed priorities are confusing

One more limitation comes from our MicroTypes definition through Media Type’s parameters and is related to priorities
between MicroTypes and parent Media Types.
Imagine the client is sending the following to the server:

Accept: application/vnd.api+json; pagination=simple-spec; querying=graphql; querying=jsonapi, application/vnd.api2+json;

Given this header, the client sets the priorities in the following order:

  1. application/vnd.api+json with the following MicroTypes
    • pagination=simple-spec
    • querying=graphql Ou bien querying=jsonapi
  2. application/vnd.api+json with the following MicroTypes
    • pagination=simple-spec
    • querying=jsonapi Ou bien querying=jsonapi
  3. application/vnd.api2+json

But how can the client prioritize (3) choice over (2) ?
Having multilevel priorities is difficult in this context and could be only solved by sending 3 options to the server,
essentially flatting and removing the MicroTypes priority scheme that we showed and falling back to the classic Media Type negotiation:

Accept: application/vnd.api+json; pagination=simple-spec; querying=graphql, application/vnd.api2+json, application/vnd.api+json; pagination=simple-spec; querying=jsonapi;

In our experience though, negotiation in HTTP is not used that extensively (although it should): clients
are usually prepared before hand for one Media Type (and its MicroTypes in our context).
Thus, we don’t think this will be an issue in practice, at least initially, until community embraces Introspectiveness and new standards are created
solving these limitations.

This is also not a breaking change per-se but it’s good to have it in mind and possibly reconsider it or alter it
when eventually patterns for MicroTypes and parent Media Types for Introspected REST APIs are settled down.

To our knowledge we haven’t broken any other HTTP-related specification for Introspected REST and the broken changes that
we had were very minor to our understanding, all of them being backwards-compatible to existing clients.
Given that the whole HTTP, related protocols and implementations, since its inception have always been based on proactive
negotiation we think that these changes are affordable for our new model.
Even when they are not affordable we feel that there are alternative ways to mitigate those limitations.
But after all, IETF, W3C and related organizations usually are not preceding implementations but instead implementations
affect and drive these specifications through the committees.
If IETF sees that people are using the specifications differently than these have been defined, they should update them
or create new ones, as long as these don’t break the existing Internet infrastructure, which they definitely do not in our case.

10.5.2. Performance considerations

Introspected REST adds some performance issues related to introspection process:
the client needs to first do a reconnaissance request to figure out what capabilities the server supports.
Then for each capability that is described by a MicroType, the client might possibly need to send another request
to retrieve the metadata of that MicroType.
This adds much more requests than regular REST APIs which would lead to increased latency until the client fetches
or sends the actual resource.

However, according to Introspected REST, the client can cache all this information using server’s caching headers,
which could be different for each MicroType.
In that way, Introspected REST could possibly be more performant than regular REST because the client might have to actually
request metadata very sparsely compared to actual data requests and given that the data responses will be much
smaller than REST’s equivalent responses (which would also hold all the necessary metadata), it should lead to better performance
à long terme
.
We should also note that Introspected REST is not ideal for all API designs and there could be cases that REST
becomes a better choice than Introspected REST.
Nevertheless, we feel that for most machine-to-machine communications Introspected REST is a better choice for all the advantages
it offers and possibly more performant than REST.

11. An Introspected REST API prototype in the world of HTTP and JSON

In the following we will describe the architecture of the Introspected REST APIs through
a proposed implementation.
The reader though should not confuse the proposed implementation details with the actual
architecture style.
This is by no means a complete Media Type, but just an example of the potential of Introspected REST.
The actual MicroTypes and Media Types will be created by the community.

For our solution, we will use JSON,
JSON Schemas,
JSON super schemas,
JSON-LD
et problem+json
each representing a different MicroType.
But the reader could apply the same ideas using any message format and spec.

Our use case will be the same as the one in section 7.1, a miniature of yet another Social App.
Given that Introspected REST differs only in HATEOAS part of REST, the identification of the resources devrait be kept the same, namely:

  • Utilisateurs resource (/users):
    • List users (GET /users): Gets a collection of Utilisateur Ressources
    • Create a new user (/users): Creates a new Utilisateur with the specified attributes.
  • Utilisateur resource (/users/id):
    • Get a user (GET /users/id): Gets the attributes of the specified Utilisateur
    • Update a user PATCH /users/id: Updates a Utilisateur with the specified attributes
    • Delete a user DELETE /users/id: Updates a Utilisateur with the specified attributes

Let’s assume that our parent Media Type is application/vnd.api+json.

11.1. Isolating the actual data from metadata

Our top priority is to offload the final response object from the metadata, like hypermedia.
Instead, we will provide to the user only the data and possibly any runtime metadata.

When the client manipulates a Utilisateur resource, the response should contain only the data:


  "user": 
    "id":"685",
    "email":"[email protected]",
    "name":"Filippos Vasilakis",
    "birth_date": "1988-12-12",
    "created_at": "2014-01-06T20:46:55Z",
    "microposts_count":50
  

Similarly, a Utilisateurs resource will be a collection of Utilisateur resources:

{
  "users": [{[{[[
    "id":"685",
    "email":"[email protected]",
    "name":"Filippos Vasilakis",
    "birth_date": "1988-12-12",
    "created_at": "2014-01-06T20:46:55Z",
    "microposts_count": 50
  , 
    "id":"9124",
    "email": "[email protected]",
    "name": "Robert Clarsson",
    "birth_date": "1940-11-10",
    "created-at": "2016-10-06T16:01:24Z",
    "microposts-count": 17,
  ]

The actual format of the data could vary regarding the root element or possibly the place of the primary id.
Such details will be described by the Media Type.
What is important here is that the data does not contain any metadata, apart from runtime metadata,
that we will describe later.

11.2 Introspection Method

For introspection method we will use the HTTP OPTIONS, as described in 10.4.1,
but with the additional description of runtime MicroTypes, which in our case do
have some introspective content for the client to fetch.
The specific semantics of this document will be described in the parent Media Type,
but it would look like this:


  "micro-types": 
    "runtime": 
      "pagination": 
        "url": "/api/users/1?microtype=pagination",
        "method": "OPTIONS",
        "content-type": "application/simple-pagination+json",
        "priority": "1.0"
      ,
      "errors": 
        "url": "/api/users/1?microtype=errors",
        "method": "OPTIONS",
        "content-type": "application/simple-errors+json",
        "priority": "1.0"
      
    ,
    "introspective": 
      "json-schema": 
        "url": "/api/users/1?microtype=json-schema",
        "method": "OPTIONS",
        "content-type": "application/schema+json",
        "priority": "0.8"
      ,
      "json-hyper-schema": 
        "url": "/api/users/1?microtype=json-hyper-schema",
        "method": "OPTIONS",
        "content-type": "application/schema+json",
        "priority": "0.8"
      ,
      "json-ld": 
        "url": "/api/users/1?microtype=json-ld",
        "method": "OPTIONS",
        "content-type": "application/ld+json",
        "priority": "0.5"
      ,
      "simple-description": 
        "url": "/api/users/1?microtype=simple-description",
        "method": "OPTIONS",
        "content-type": "application/json",
        "priority": "0.2"
      
    
  ,
  "documentation": 
    "url": "/documentation#user",
    "method": "OBTENIR",
    "content-type": "text/html"
  

Each entry describes the url which the client can access it, the HTTP method
the client should use along with the Media Type of the expected response.
Finalement, le priorité number specifies the preceding order of each MicroType
in case a functionality is described by one or more MicroTypes.

Note that the Media Type of the introspective content will be described
by the MicroType the client tries to access.
As a result, if a client doesn’t recognize a MicroType, it wouldn’t try to access
it anyway.

11.2. Runtime Metadata

It goes without saying that when a client requests a collection of resources,
it expects some kind of pagination with it.
For pagination MicroType we have a number of different options.
One option is to use the Lien header and define the links there, in a representation-agnostic way.
But given that our application is intended to powerful clients that would also parse the JSON body
we wouldn’t gain much, possibly we would make things even more complex for them.

Another possibility, with some inspiration from JSONAPI, is to use the premier, dernier, prev et suivant spécifier
the first, last, previous and next page respectively.

{
  "users": [{[{[[
    "id":"685",
    "email":"[email protected]",
    "name":"Filippos Vasilakis",
    "birth_date": "1988-12-12",
    "created_at": "2014-01-06T20:46:55Z",
    "microposts_count": 50
  , 
    "id":"9124",
    "email": "[email protected]",
    "name": "Robert Clarsson",
    "birth_date": "1940-11-10",
    "created-at": "2016-10-06T16:01:24Z",
    "microposts-count": 17,
  ],
  "meta":
    "self":"/api/users?page=2&per_page=10&offset=0",
    "first":"/api/users?page=1&per_page=10&offset=0",
    "prev":"/api/users?page=1&per_page=10&offset=0",
    "next":"/api/users?page=2&per_page=10&offset=0",
    "last":"/api/users?page=9&per_page=10&offset=0"
  

A different approach is to just specify
la page, per_page et décalage to the client and also provide a URI template to
use with those values to access any page.

{
  "users": [{[{[[
    "id":"685",
    "email":"[email protected]",
    "name":"Filippos Vasilakis",
    "birth_date": "1988-12-12",
    "created_at": "2014-01-06T20:46:55Z",
    "microposts_count": 50
  , 
    "id":"9124",
    "email": "[email protected]",
    "name": "Robert Clarsson",
    "birth_date": "1940-11-10",
    "created-at": "2016-10-06T16:01:24Z",
    "microposts-count": 17,
  ],
  "meta": 
    "page": 2,
    "per_page": dix,
    "offset": 0
  

We could provide the URI template when introspecting the pagination MicroType:


  "link": "/api/resource?page, per_page, offset"

Of course, the MicroType spec would specify how the client should parse and determine
the pagination links from this introspective content.
In that way, we don’t treat the clients as stupid but smart enough to understand
what they need to do on their part to get what they want.

Which is the best solution? It depends, and that’s why we should embrace MicroTypes.
le Lien header-based solution is representation-agnostic and could benefit some clients
that don’t deal much with the content but in practice such clients are very rare, especially in our use case.
The second solution would limit the client application developer if she wanted to have a link
of a specific page, while the last solution would limit the API designer to avoid
having the number of total pages in the response, because it could be a huge cost to the database level.
In any case, Introspected REST doesn’t restrict us to specify two or more alternative MicroTypes for the same API
fonctionnalité
, like pagination.

11.2.2 The Errors MicroType

When the API is supposed to return an unexpected response to the user, like a 4xx or 5xx error,
the response will have a different structure than the resource that the client requested.

Usually the semantics of an error respond are defined in the API’s Media Type but we will use the newly-published RFC 7807 (Problem Details for HTTP APIs),
which defines the problem+json Media Type for JSON HTTP APIs.
To give an example how the response will seem when following this RFC,
imagine that when updating a User object, the application developer might wrongly send an invalid birth_date.
Then the application should respond with the following structure:


  "Titre": "The birthdate has an invalid format.",
  "details": "The birthdate must be in the format of 1985-04-12T23:20:50.52Z.",
  "status": 422

If you inspect the spec you will notice that the spec limits us by omitting specifying a way to associate an error message with a specific resource attribute.
As a result, we can only specify the falsy attribute in the title or details attribute of the error object, which are human-targeted,
and thus informing only the end user and not the client.
We could add extension members, as the spec suggests, to customize the error object in our needs but the final response object wouldn’t be
self-descriptive, unless we customly extended it.

The good thing though is that normally such errors should be caught on the client-side by the introspected MicroTypes for the resource structure,
which in our use case are the schema validations from the JSON Schema MicroType.
The error object could be used for more advanced errors, like the following:


  "Titre": "Transaction failed",
  "details": "The remaining amount of virtual coins in your account is not enough for this purchase",
  "status": 403

Another thing that we should take care is the fact that this RFC requires returning a different Media Type than the one used by the API.
In theory the API’s Media Type explain how the errors work using the same semantics as defined in problem+json RFC but the RFC
suggests using application/problem+json for Media Type.

The data model for problem details is a JSON [RFC7159] object; quand
formatted as a JSON document, it uses the “application/problem+json”
media type.

— RFC 7807

However in order for this to work the client needs to negotiate it and accept this Media Type,
otherwise we have a gap in the client-server communication
.
The client can’t be asking for the API’s Media Type and unexpectedly receive the application/problem+json
Media Type.

In HTTP that would be achieved using the Acceptez header, which could look like that:

Accept: application/vnd.api+json, application/problem+json

But that reminds us the concept of (runtime) MicroTypes, right?
Even the negotiation looks very similar.
To that extend, creating a wrapper MicroType shim around this Media Type, that other API designers
can also use, should be effortless.

To take one step further, given that such error information is crucial for the user to understand why her action is not advancing,
we feel that the client should be able to négocier the errors MicroType, that is, the information and structure of the
returned errors object.
Some clients might need the most basic error information and use only the HTTP status code, other clients might
be interested in as much possible information available in order to show it to the user.
For instance, the client might show preference to another problems Media Type before falling back to problem+jsoncomme
seen in the following Accept header example:

Accept: application/vnd.api+json, errors=problem/extensive+json, errors=problem+json;

11.3. Introspective Metadata

We will describe our APIs capabilities by mixing together different MicroTypes targeted each one for a specific capability
of our API, following the Single Responsibility Principle.
The client will be able to retrieve the information of each metadata MicroType by introspecting the resource.

11.3.1. Structural metadata

One of the most important things for a client to know is the expected structure of the request/response resource object
along with information on the data types.
For that we will use JSON Schemas, a powerful spec that enables you to describe and validate your JSON data.

Given that this specification has been published using the RFC method and taking into account its popularity,
it is very probable that there est
an implementation for that MicroType for the client’s environment.
Also, a cool side effect of having the structure definition of the resource as a MicroType available through resource’s introspection,
is that the client can use this information to first validate the object before sending it over the wire to the server.

11.3.1.1. User resource
{
  "$schema":"https://json-schema.org/draft-04/schema#",
  "$id":"https://example.com/user.json",
  "Propriétés":{
    "user":
      "type":"objet",
      "Propriétés":
        "id":
          "maxLength":64,
          "type":"chaîne"
        ,
        "email":
          "maxLength":255,
          "type":"chaîne",
          "format":"email"
        ,
        "name":
          "maxLength":255,
          "type":[[[["null", "chaîne"]
        ,
        "birth_date":
          "type":"chaîne",
          "pattern":"^[0-9]4-[0-9]2-[0-9]2$"
        ,
        "created_at":
          "maxLength":255,
          "type":"chaîne",
          "format":"date-time"
        ,
        "microposts_count":
          "type":"entier"
        
      ,
      "Champs obligatoires":[[[[
        "id",
        "email",
        "name",
        "birth_date",
        "created_at",
        "microposts_count"
      ]
    
  },
  "Champs obligatoires":[[[[
    "user"
  ],
  "type":"objet"
}
11.3.1.2. Users resource

Note that the Users resource is just a collection of User object and as a result
it references the User schema.


  "$schema":"https://json-schema.org/draft-04/schema#",
  "$id":"https://example.com/users.json",
  "Propriétés":
    "users":
      "type": "array",
      "$href": "https://example.com/user.json#/properties/user"
    ,
    "meta": 
      "type":"objet",
      "page": 
        "type": "entier",
        "default": 0,
        "le minimum": 0,
        "$ref": "#/definitions/extra"
      ,
      "per_page": 
        "type": "entier",
        "le minimum": 1,
        "maximum": 100,
        "default": 50
      ,
      "offset": 
        "type": "entier",
        "le minimum": 0,
        "default": 0
      ,
      "Champs obligatoires":[[[[
        "page",
        "per_page",
        "offset"
      ],
    
  ,
  "Champs obligatoires":[[[[
    "users",
    "meta"
  ],
  "type":"objet"

11.3.1.3. Request Response inconsistency

Although here we have the same object semantics for request and response object, in theory these could be different.
If that’s the case, we should denote each object in the response parented under
distinct JSON attributes (like accepte/produit ou accepte/résultats).

11.3.2. Hypermedia metadata

For the Hypermedia part we will use JSON Hyper Schemas.
Specifically we will use the draft V4 of JSON Hyper Schemas as the
next versions (V5, V6) are targeted to hypermedia APIs that
are HTML-equivalents. For instance, there is no way we can define a méthode attribute, restricting us to OBTENIR et POSTER
depending whether there is a body to send or not.
In the Introspected REST terminology, V5 and V6
provide hypermedia semantics only for forms and not actions.

Resource schemas defined in the previous section are referenced by the following Hyper Schemas, in order to avoid
duplication of our metadata.
Such functionality would have to be described by both MicroTypes.

11.3.2.1. User resource

  "$schema":"https://json-schema.org/draft-04/schema#",
  "$id":"https://example.com/user-links.json",
  "Propriétés":
    "$href": "https://example.com/user.json#/properties"
  ,
  "links": [[[[
    
      "rel": "microposts",
      "href": "/microposts?user=userId&page=page&per_page=per_page&offset=offset",
      "hrefSchema": 
        "allOf": [[[[
          
            "$ref": "https://example.com/users.json#/properties/meta"
          ,
          
            "$ref": "https://example.com/users.json#/properties/user/id"
          ,
        ]
      
    ,
    
      "rel": "update-user",
      "href": "/users",
      "method": "PATCH",
      "targetSchema": 
        "$ref": "https://example.com/user.json"
      
    ,
    
      "rel": "delete-user",
      "href": "/users",
      "method": "DELETE",
      "targetSchema": 
        "$ref": "https://example.com/user.json"
      
    
  ]

11.3.2.2. Users resource

  "$schema":"https://json-schema.org/draft-04/schema#",
  "$id":"https://example.com/users-links.json",
  "Propriétés":
    "$href": "https://example.com/users.json#/properties"
  ,
  "links": [[[[
    
      "rel": "self",
      "href": "/users?page=page&per_page=per_page&offset=offset",
      "hrefSchema": 
        "$ref": "https://example.com/users.json#/properties/meta"
      
    ,
    
      "rel": "create-user",
      "href": "/users",
      "method": "POST",
      "targetSchema": 
        "$ref": "https://example.com/user.json"
      
    
  ]

Notice that we also define here the pagination, by referencing parts of the user’s méta objet.
Our strategy is duplicate common functionality in MicroTypes, wherever we can, in order to help
our clients. Possibly not all clients will be programmed for all our MicroTypes, especially if we
release them progressively.

11.3.4. Descriptions metadata

For human-targeted information, we could use a custom MicroType that describes each attribute of the response object.
Notez que this information must not be required to parse and understand the API but to use the API data on our application domain.
For instance, understanding that when updating the email attribute an email is triggered to inform the user for the change,
is not part of the API client responsibility but it’s vital for the application developer to to know what to expect from it.


  "user": 
    "id": 
      "Titre": "The identifier of the resource.",
      "la description": [[[[
        "This identifier should not be exposed to the user, to avoid any confusions."
      ]
    ,
    "email": 
      "Titre": "The primary email of the user's account",
      "la description": [[[[
        "The email is used for any transactional email.",
        "Also, the same email is used when user authenticates to the system.",
        "Please note that whenever you update the email, user receives an automated email describing the change"
      ]
    ,
    "name": 
      "Titre": "The user's full name (first and last name concatenated)",
      "la description": [[[[
        "This field could be empty or null.",
        "If so, the application should show the email instead for the user's name."
      ]
    ,
    "birth_date": 
      "Titre": "The date of birth of the user",
      "la description": []
    ,
    "microposts_count": 
      "Titre": "The number of published microposts the user has.",
      "la description": [[[[
        "Please note that due to caching this number could have a small delay to reflect the actual number",
        "The application should either inform the user about that or make sure it manually updates the microposts counter after publishing/deleting a micropost after publishing/deleting a micropost."
      ]
    
  

This metadata will be used for the documentation generation, as we will see in section 11.7.

11.3.5. The case of a non-compatible spec for introspection: Linked Data metadata using JSON-LD

For denoting the semantic meaning of each attribute of our resources we will employ JSON-LD.
It should be noted that JSON-LD spec was developed with the goal to require as little effort as possible from developers
to transform their existing JSON to JSON-LD but also to not require breaking changes to your
existing API, which makes it backwards compatible with any current deployed API.
This conflicts with our design of introspection because having contexts without the data would break the spec.
As a result we have the following 2 options.

11.3.5.1. Extending spec by creating a Shim MicroType

Our first option is to create a wrapper shim MicroType that defines how the spec should work
for the clients to parse and understand the data, with the least possible changes.
A naive shim, that we show here, would output the context information in the introspected process.
Then the client should match this information in combination with the runtime data.

11.3.3.1. User resource

  "@context": 
    "@vocab": "https://schema.org/",
    "@type": "La personne",
    "birth_date": "birthDate",
    "created_at": "dateCreated",
    "microposts_count": nul
  

11.3.3.2. Users resource

  "@context": 
    "@vocab": "https://schema.org/",
    "birth_date": "birthDate",
    "created_at": "dateCreated",
    "microposts_count": nul
  ,
  "@graph": [[[[
    
      "@type": "La personne"
    
  ]

11.3.5.2. Considering it as runtime metadata

Our second option is to exploit the IATEOAS principles regarding runtime metadata
and append them inside the response by considering them as object-specific runtime metadata.
However, we feel that such decision should be taken only if nothing else is possible,
given that in Introspected REST data and metadata should be distinctively separated.

11.7. Automating the documentation generation

The documentation of our API should be a dedicated page under the API’s URL namespace (i.e. /api),
by returning a regular web page, targeted to humans and not machines.
The technical details is out of the scope of this prototype example but we
can’t stress enough that the generated documentation should mostly use information from MicroTypes available for the machines,
programmatically wrapped in a human-friendly format.

12.1. GraphQL

GraphQL is a data query language that was created by Facebook and released to the public
in 2015.
The specification of the query language is not tied to the protocol used
underneath or the message format, although HTTP in combination with JSON is usually used.
What is different about GraphQL is that it makes the client’s requirements and performance
as a top priority
, regardless of the internal implementation of the data layer in the server.
As a result, front-end engineers tend to love it due to its expressiveness that
usually is not found in REST APIs.

For instance, retrieving a Utilisateur object with a subset of it’s attributes, along
with some microposts ordered by creation date, is very easy, given that the server
implementation support those filters:


  utilisateur(identifiant: "1") 
    prénom
    email
    birth_date
    microposts (limite: dix, orderBy: created_at)
      Titre
    
  
}

The query not only specifies what the client wants to retrieve but also it specifies
the structure the response should have.
Also, GraphQL supports an introspection process that clients can use in order to figure
out the available fields of each resource along with other useful information, like
data types, the available operations those resource support (mutations in GraphQL terminology) etc.

GraphQL solves common issues in networked APIs in a radical, unique way.
Facebook engineers figured out that instead of trying to adapt
existing Internet infrastructure and protocols to their needs, they designed
a query language that solves their problems and use HTTP as a dumb pipe to do the hard work of
communicating both queries and data.
In terms of REST principles, GraphQL responses are both evolvable and self-descriptive, as GraphQL’s
introspection is very powerful allowing the clients to learn anything that is needed
about the resources.

However, we feel that GraphQL does have some costs and it’s not a solution that any
business can apply.
First, GraphQL doesn’t play well with the existing HTTP infrastructure.
For instance, most GraphQL implementations, use a single endpoint with the same
HTTP method, POSTER, for the client-server communication.
As a result, the specification cannot take advantage of existing HTTP protocols
and mechanisms but instead has to re-invent the wheel on some of them, like caching.

Also, adding GraphQL to an existing service has huge costs.
Although there are libraries for most languages and frameworks that facilitate the development of
GraphQL API, this is not always the case.
But apart from that, the server engineer must take full responsibility for supporting all
kind of queries the client might need and at the same time these queries need to be efficient and scalable.
When we can know in advance what are the limits of a query, we are able to optimize for it,
however, with GraphQL, a client can send any query using any of the all the possible resources and structure them
in a way that for the server is random.
In such cases, it’s impractical to optimize beforehand and solving scaling issue becomes
a real challenge that possibly only companies with huge amount of resources can really afford.

And again, as with existing Media Types design, GraphQL creates a closed silo in our API and differentiating from the existing
spec is nearly impossible.
For instance, if we need to support an additional data type, it’s impossible
because we are dependent to the existing libraries and creating our own GraphQL library would require too much time.
But even if that was solved, a possible modification in the current spec would probably break most existing clients.
We feel that a MicroType-based architecture is more powerful than a specification that, although powerful,
limits the users to its semantics.

The fact that REST API designers haven’t treated very well front-end
engineers in the past, in combination with the complexity a modern
REST API could have, has given a lot of space to GraphQL to rise as one of the most prominent
API designs.
Although GraphQL is a great asset to have it around, we don’t think that it’s practical for
all API cases, but instead it mostly suits best big companies that can afford the costs.
The API designer must balance the trade off between the cost of development and the
client’s affordances.

With Introspected REST and a number of powerful MicroTypes it is possible to replicate the existing GraphQL
specification and even leverage existing HTTP infrastructure.
In fact, we feel that Introspected REST is far more powerful than GraphQL:
not only it gives you the ability to balance the costs of implementation and client performance,
but also it can support multiple, different, querying specs for different classes of clients,
all these by leveraging the existing HTTP infrastructure.

12.2. Linked Data and Semantic Web

Linked data and semantic web has been trying to solve the problem of mutual understanding
between machines many years now.
Using a pre-defined vocabulary, machines can determine the type of a resource, like if
it’s a person, an employee, an athlete or
even the types of each attribute of a resource, like a name, an email etc.
It is a step close to have self-descriptive APIs that machines can understand and process.
For instance, using JSON-LD as we saw earlier, we can specify all attributes of a Utilisateur resource:


  "user": 
    "@context": 
      "@vocab": "https://schema.org/",
      "@type": "La personne",
      "birth_date": "birthDate",
      "created_at": "dateCreated",
      "microposts_count": nul
    ,
    "@id":"685",
    "email":"[email protected]",
    "name":"Filippos Vasilakis",
    "birth_date": "1988-12-12",
    "created_at": "2014-01-06T20:46:55Z",
    "microposts_count":50
  

Moreover, modern specifications like JSON-LD allow us to omit
the definitions from the response’s data and instead provide only a link to
a publicly accessible directory that a machine can déréférence,
similarly to the introspection method of Introspected REST.
The resource ony needs to have the vocab attribute inside JSON-LD’s le contexte.


  "user": 
    "@context": 
      "@vocab": "https://example.com/my-custom-schema/"
    ,
    "@id":"685",
    "email":"[email protected]",
    "name":"Filippos Vasilakis",
    "birth_date": "1988-12-12",
    "created_at": "2014-01-06T20:46:55Z",
    "microposts_count":50
  

The idea of semantic web can be found even in real life.
In an example taken by Donald Norman, and
often quoted by Mike Amundsen’s talks,
in real life when we see a door, we know instantly how it opens because we have associated
the design of the door with its opening mechanism:
if a door has a bar across it then we push while if there is a little handle in the door then we pull.

While semantic web allow us to associate resources in the web with entities that hold
metadata and have specific properties, in Introspected REST we ask the door itself how its mechanism works:
using the door’s metadata we can learn how to open any door and eventually we can even
open doors whose opening mechanism we have never seen before.

In any case, in Introspected REST we embrace semantic web by employing the necessary MicroTypes
and we don’t really feel that this work is related to Introspected REST in a competing sense
but instead, both concepts could complement each other.
In fact, we feel that using linked data is just great and API designers should employ it more often.

12.2.1 Hydra

To that extend, Markus Lantaler developed Hydra, which not only allows us to associate
common resources and attributes with their representation but also RESTful hypermedia
concepts on them, like actions (called operations), links, status codes etc.

So again in our use case, we can specify some actions using Hydra:


  "@context": [[[[
    "http://www.w3.org/ns/hydra/core",
    
      "@vocab": "https://example.com/my-custom-user-vocab",
      "@type": "La personne",
      "birth_date": "birthDate",
      "created_at": "dateCreated",
      "microposts_count": nul
    
  ],
  "@id":"685",
  "email":"[email protected]",
  "name":"Filippos Vasilakis",
  "birth_date": "1988-12-12",
  "created_at": "2014-01-06T20:46:55Z",
  "microposts_count":50,
  "operation": 
    "@type": "UpdateUser",
    "method": "PATCH",
    "expects": 
      "@id": "https://example.com/my-custom-user-vocab",
      "supportedProperty": [[[[
        
          "@type": "email",
          "property": "email",
          "Champs obligatoires": faux
        ,
        
          "@type": "name",
          "property": "name",
          "Champs obligatoires": faux
        ,
        
          "@type": "birthDate",
          "property": "birth_date",
          "Champs obligatoires": faux
        
      ]
    
  

Again, Hydra’s-specific content, like operations, can become dereferencable thus
making response’s load much smaller
, although this is not a requirement as in
Introspected REST.

Although we support initiatives that allow API designers to serve metadata on the side,
like Hydra does with dereferencable content, we can’t miss the fact that Hydra
has become a very complex specification and still it’s type system is a weak one.
We firmly believe that MicroTypes for actions (Hydra’s equivalent to operations)
will be much more powerful than hydra’s semantics of Champs obligatoires, en écriture, lisible,
and soon an API designer will be able to choose one MicroType from the same class of
MicroTypes, like data types, that fits best for her.
Specifications that try to define everything in one place, like Hydra does, limit
the API designers a lot and eventually such specs
deliver an mediocre set of choices to choose from.

In any case, we feel that Hydra spec is one of the few API specs that significantly
differs from the conventional API specs and can provide almost completely self descriptive
les réponses
, an unfortunately rare key property of modern APIs.

Similar to the profile media type parameter
that Toby A. Inkster had proposed in 2009, Erik Wilde suggested a profiling mechanism
of the underlying Media Type through the HTTP Link header, that was later
published as RFC 6906.

A profile is defined not to alter the
  semantics of the resource representation itself, but to allow clients
  to learn about additional semantics (constraints, conventions,
  extensions) that are associated with the resource representation, in
  addition to those defined by the media type and possibly other
  mechanisms.

— RFC 6906

Essentially, the profile parameter, given that the client understands it, would define Additionnel semantics of
the response’s representation that are not defined through the Media Type used.
The information for the additional semantics would be found in all responses regardless the client but only
the “smarter” clients would be able to parse, understand and use this information whereas the rest would just ignore it.

This link relation type is similar to our work of MicroTypes but unfortunately fails to advocate towards reusable profiles.

While this specification
  associates profiles with resource representations, creators and users
  of profiles MAY define and manage them in a way that allows them to
  be used across media types; thus, they could be associated with a
  resource, independent of their representations (i.e., using the same
  profile URI for different media types). However, such a design is
  outside of the scope of this specification, and clients SHOULD treat
  profiles as being associated with a resource representation.

— RFC 6906

By having profiles attached to specific Media Types results in much less adoptability and flexibility and fails to signal the
actual practicability of such architecture.
However, if profiles take the conceptual form of independent MicroTypes, then the clients can negotiate for those and eventually choose
the one that fits best.
Although the negotiation part is skipped from the RFC, we feel that such works are towards the right direction that will allow us
to build evolvable, self-described APIs.

12.4. JSON Home

Note that JSON Home is still in a draft state.

JSON home is a draft specification
that defines a “home document” format for non-browser HTTP clients to first request in order to discover
the server’s capabilities that it support.
Specifically, the document specifies semantics to describe the API itself (like author, documentation link etc)
along with its resources.
For each resource, the document can provide a link for the client to access it directly (instead of
figuring out the link using REST state transitions) and more information, mostly hints, like
permitted methods, media types etc.

It should be noted that JSON Home it’s one of the few specifications along with RFC 7807 (Problem Details for HTTP APIs)
and possibly Linksets that
because of their semantics and specifications, they slide away from Roy’s REST model and
acknowledge the distinction between
browser-based clients that are driven by real humans, and non-browser, machine based-clients and suggests
that the latter should be treated differently.

As the draft notes the benefits of using such a home document are multifold:

o Extensibility – Because new server capabilities can be expressed
     as link relations, new features can be layered in without
     introducing a new API version; clients will discover them in the
     home document. This promotes loose coupling between clients and
     les serveurs.

o Evolvability – Likewise, interfaces can change gradually by
     introducing a new link relation and/or format while still
     supporting the old ones.

o Customisation – Home documents can be tailored for the client,
     allowing different classes of service or different client
     permissions to be exposed naturally.

o Flexible deployment – Since URLs aren’t baked into documentation,
     the server can choose what URLs to use for a given service.

o API mixing – Likewise, more than one API can be deployed on a
     given server, without fear of collisions.

— Home Documents for HTTP APIs, draft 06

Although we can instantly see the benefits of such structure, we believe that a specification like JSON Home
is very weak. Specifically, it is tied to JSON message format which, although very popular, could possibly be
inappropriate in some use cases.
Instead, a better idea would be to define the necessary attributes and semantics that a Home document
should provide and then let the API designer to choose if these will be implemented in JSON, XML or binary format.
Such architecture would be more robust and would give more options to an API designer.

Secondly, the document resource hints are very abstract and generic that probably are not sufficient
for the client to parse them without some documentation.
Our work makes use of MicroTypes that allows the API designer to offload such information
to more specific formats, possible duplicated in multiple specs to support as many as possible
clients, but also to let the clients select the most appropriate MicroType(s) for them.
We firmly believe that a MicroType-based architecture is much more powerful than a simple,
semantically identical for all APIs, JSON-specific, home document
.

However we can’t neglect the fact that influencing engineers are finally recognizing the
dissimilarity of browser-based, driven by humans clients and machine-based clients.
In fact, carving our infrastructure for machine-based clients to be similar with human-driven clients we
underestimate their capabilities: machines can be much more powerful and smart than humans.

12.5. RESTful API Description Languages

Over the past years, there has been a trend on creating API documentation through specialized tools, like OpenAPI specification (ex. Swagger).

As we have already noted, in a REST API documentation, in the sense of offline contracts,
shouldn’t even exist and thus such approach is fundamentally wrong.
By giving so much weight on the documentation but at the same time treating it as something different, separated from the code
leads to inconsistencies between the actual API and the API description.
Those tools have been improved so much lately that now allow us to write the documentation and let them generate
the basis of our code, depending on our language/framework, which could fix the inconsistencies issues.
Unfortunately though, such approach leads to an RPC design instead of a hypermedia-based system.

We believe that API designers are limited by marrying these tools.
The tools themselves have limitations,
but also, having tools that aim to provide all-in-one to the API designer is against our philosophy: tools should do one thing and do it well.

12.6. API directories

Another trend for APIs is to register them in an online service, called API dictionary and possible push there the API documentation as well.
We feel that this is not a very helpful structure. APIs should be discoverable by themselves without using centralized services.
The API’s conceptual root url should provide everything that is needed, and using already published protocols
like WebFinger, which builds upon Well-Known Uniform Resource Identifiers RFC
and can give API information for client bootstraping.

13. Conclusion

The best software architecture “knows” what changes often and makes that easy.

— Paul Clements

Is it the API spec designers to blame for creating non self-descriptive REST specifications or did they make a deliberate choice
to avoid fully support the HATEOAS constraint of REST and instead delegate such information to documentation?

In the research of a fully REST API, we determined that REST is complex and
inflexible.
Moreover, its adaptation in HTTP goes through the mechanism of Media Types
which specifies the whole API vocabulary in a sole place
while HATEOAS need to bear the brunt of communicating to the client the available
capabilities of the server, based on Media Type’s vocabulary,
for each resource.
The result is very complex API responses that tangle together hypermedia with data making development a real
challenge for both the client and the server.

Our new model, Introspected REST, which solves most of REST issues, steps on Roy’s initial model
but takes a different path regarding hypermedia. It requires the server to deliver only
data when a resource is requested, stripping out any HATEOAS-related information and instead deliver
the hypermedia on the side whenever a client needs it.
However, Introspected REST goes beyond conventional hypermedia by supporting more classes of metadata, other
than hypermedia, through our concept of MicroTypes which act as small reusable components of API
fonctionnalité.
MicroTypes are not intended to be used only for allowing the client to derive the application state
at runtime. They also make the clients smarter by allowing them to take an active role
in the client-server communication
while enabling them to provide essential feedback to the application layer.

We firmly believe that Introspected REST is the key to evolvable services of the future that are accessed by unmanned clients
with a lifespan of decades.
Our model allows the API designers to fine-tune the flexibility and extensibility of the API to their needs,
even progressively or asymmetrically for different classes of clients.
Choosing between REST or GraphQL won’t be necessary as our model can support both styles simultaneously,
using multiple sets of MicroTypes.

Commentaires

Laisser un commentaire

Votre commentaire sera révisé par les administrateurs si besoin.