REST jusqu'au bout : à quoi sert vraiment HATEOAS sur une API métier ?
Beaucoup d'APIs se disent REST alors qu'en pratique elles exposent surtout du JSON sur HTTP.
Des routes propres, des verbes HTTP corrects, des statuts bien utilisés, parfois un peu de pagination. C'est déjà mieux qu'une API bricolée autour de POST /doSomething.
Mais ce n'est pas encore le sujet le plus exigeant de REST.
Le point vraiment distinctif, celui qu'on voit rarement poussé jusqu'au bout, c'est HATEOAS : Hypermedia As The Engine Of Application State.
Dit autrement : une API ne se contente pas d'exposer des données. Elle expose aussi les transitions possibles depuis l'état courant. Elle indique au client ce qu'il peut faire ensuite, où, et parfois sous quelle forme.
Sur le papier, ça ressemble vite à un vieux débat d'architectes web.
En pratique, le sujet redevient intéressant dès qu'un système porte :
- beaucoup de règles métier ;
- plusieurs workflows ;
- plusieurs rôles ;
- et plusieurs interfaces front à maintenir en parallèle.
Dans ces contextes, HATEOAS n'est plus juste une coquetterie REST. C'est une façon de déplacer une partie de la connaissance métier de chaque client vers l'API elle-même.
Le malentendu classique sur REST
Quand on parle de REST, beaucoup d'équipes pensent surtout à trois choses :
- des ressources ;
- des URI lisibles ;
- les verbes HTTP.
Ce socle est utile, bien sûr.
Mais il manque souvent le point qui fait la différence entre une API "HTTP bien rangée" et une API réellement hypermedia-driven : le client ne devrait pas avoir à connaître à l'avance tout le graphe de navigation métier de l'application.
Dans une vision REST poussée jusqu'au bout, le client entre par un point d'entrée connu, puis découvre les prochaines actions à partir des représentations reçues.
C'est exactement ce que fait un navigateur sur le web : il part d'une page, suit des liens, soumet des formulaires, avance dans un flux. Il n'a pas besoin d'avoir codé en dur toute l'arborescence du site avant d'afficher la première page.
Sur une API métier, l'idée est la même, mais appliquée à des représentations machine-friendly.
HATEOAS, concrètement, c'est quoi ?
Le principe est simple à formuler : la réponse de l'API contient non seulement l'état courant, mais aussi les actions ou relations pertinentes depuis cet état.
Par exemple, au lieu de renvoyer seulement ça :
{
"id": "ORD-123",
"status": "PAID",
"total": 249.00
}on peut renvoyer quelque chose de ce genre :
{
"id": "ORD-123",
"status": "PAID",
"total": 249.00,
"links": [
{ "rel": "self", "href": "/orders/ORD-123" },
{ "rel": "invoice", "href": "/orders/ORD-123/invoice" },
{ "rel": "customer", "href": "/customers/CUS-88" }
],
"actions": [
{
"name": "refund",
"method": "POST",
"href": "/orders/ORD-123/refunds"
}
]
}La différence n'est pas cosmétique.
Dans la première version, le front doit déjà savoir qu'une commande payée peut être remboursée, à quelle URL, avec quelle méthode, et dans quelles conditions.
Dans la seconde, l'API lui dit : voilà l'état courant, voilà les relations utiles, voilà l'action actuellement autorisée.
Et si la commande passe à l'état SHIPPED, l'API peut décider de ne plus exposer refund, mais plutôt request-return.
Le client n'a plus à embarquer toute la matrice des transitions en dur pour savoir quels boutons afficher ou quelles opérations tenter.
Pourquoi ce principe existe
Le but d'HATEOAS n'est pas de "faire joli" dans les réponses.
Le but est de réduire le couplage entre le client et des détails qui changent souvent plus qu'on ne le croit :
- la structure exacte des URI ;
- la manière d'enchaîner les actions ;
- les transitions autorisées selon l'état ;
- les formulaires ou payloads attendus pour certaines opérations ;
- la présence ou l'absence d'options selon le rôle, le contexte ou la politique métier.
Sans hypermedia, toute cette connaissance vit souvent dans plusieurs endroits à la fois :
- dans la documentation ;
- dans le code du front web ;
- dans le mobile ;
- dans le backoffice ;
- parfois dans un intégrateur tiers ;
- et, bien sûr, côté API.
Le jour où une règle change, il ne faut pas seulement corriger le backend. Il faut aussi réaligner tous les clients qui avaient embarqué l'ancienne lecture du workflow.
HATEOAS cherche à limiter ce problème en disant : les prochaines transitions doivent être décrites par la représentation courante.
Ce que l'hypermedia apporte vraiment
Le bénéfice principal n'est pas technique au sens "framework".
Le vrai bénéfice est architectural : l'API devient un guide de navigation métier, pas seulement un point d'accès aux données.
C'est particulièrement utile quand les actions disponibles dépendent de plusieurs dimensions en même temps :
- état courant de l'objet ;
- rôle utilisateur ;
- permissions fines ;
- contraintes temporelles ;
- règles de conformité ;
- dépendances avec d'autres objets ;
- étapes précédentes déjà validées ou non.
Dans ce type de système, la question importante n'est pas seulement "à quoi ressemble la ressource ?".
La vraie question, c'est souvent : que peut-on encore faire, ici et maintenant, sur cette ressource ?
Et c'est précisément le genre de réponse qu'un modèle hypermedia peut porter proprement.
Non, ajouter un self link partout ne suffit pas
C'est un point important.
Beaucoup d'implémentations "HATEOAS" se limitent à ajouter quelques liens : self, next, prev, parfois related.
Ce n'est pas inutile. Pour la pagination, les collections ou les relations entre ressources, c'est même une bonne base.
Mais ce n'est pas encore l'usage le plus intéressant dans un produit métier.
Le vrai saut se produit quand l'API expose aussi des affordances :
- les actions possibles ;
- les méthodes autorisées ;
- les cibles à appeler ;
- parfois la structure attendue des champs ;
- et idéalement le sens métier de ces transitions.
Autrement dit : pas seulement "où aller", mais aussi "quoi faire maintenant".
Jusqu'où peut-on pousser l'approche ?
Il existe plusieurs niveaux de maturité dans l'usage de l'hypermedia.
1. Le niveau minimal : liens de navigation
On expose des liens utiles : self, collection, next, prev, related, etc.
C'est déjà bien pour rendre une API plus lisible et moins dépendante d'URI bricolées côté client.
2. Le niveau intermédiaire : relations et sous-ressources mieux décrites
On structure les relations entre objets et les points d'accès associés.
Par exemple :
- la relation entre une commande et son client ;
- entre un article et ses commentaires ;
- entre un dossier et ses pièces justificatives ;
- entre un ticket et son historique.
À ce niveau, l'API commence à être plus navigable que simplement "documentée".
3. Le niveau intéressant : actions métier exposées
Là, l'API ne renvoie plus seulement des liens, mais aussi les transitions métier possibles.
Exemples :
approve;reject;cancel;publish;reopen;submit-for-review.
Et ces actions n'apparaissent pas toujours. Elles dépendent de l'état courant.
4. Le niveau le plus poussé : formulaires, templates, contraintes
Ici, l'API décrit aussi comment exécuter l'action :
- méthode HTTP ;
- cible ;
- type de contenu ;
- champs attendus ;
- paramètres obligatoires ;
- parfois options autorisées.
À ce stade, on est beaucoup plus proche de l'esprit du web : la représentation ne contient pas seulement de l'information, elle contient aussi de quoi poursuivre le flux.
HAL, JSON:API, Siren : même famille, ambitions différentes
Il n'existe pas un unique format magique pour faire de l'hypermedia en JSON.
C'est d'ailleurs une des raisons pour lesquelles le sujet est moins adopté qu'on pourrait l'imaginer : il faut choisir un style de représentation et assumer son niveau d'ambition.
En simplifiant :
- HAL est plutôt léger et pratique pour exposer des liens et des ressources embarquées ;
- JSON:API structure bien les ressources, relations et liens, avec de bonnes conventions pour les collections et les relationships ;
- Siren va plus loin sur la description d'actions, ce qui le rend souvent plus intéressant pour des workflows métier.
Le bon choix dépend moins d'un effet de mode que de la question suivante : ai-je seulement besoin de navigation, ou ai-je besoin d'exposer de vraies transitions métier ?
Exemples concrets : la même commande en HAL, JSON:API et Siren
Pour voir la différence, prenons exactement le même cas métier : une commande ORD-123, déjà payée, liée à un client, avec facture disponible et possibilité de remboursement.
1. HAL
HAL est utile quand tu veux rendre ton API plus navigable sans trop charger la représentation. Le cœur du format, c'est _links, et éventuellement _embedded pour embarquer des ressources liées.
{
"id": "ORD-123",
"status": "PAID",
"total": 249.00,
"_links": {
"self": { "href": "/orders/ORD-123" },
"customer": { "href": "/customers/CUS-88" },
"invoice": { "href": "/orders/ORD-123/invoice" },
"refund": { "href": "/orders/ORD-123/refunds" }
},
"_embedded": {
"customer": {
"id": "CUS-88",
"name": "Acme Corp",
"_links": {
"self": { "href": "/customers/CUS-88" }
}
}
}
}Ce que ça apporte :
- des liens de navigation simples ;
- une manière claire de relier des ressources ;
- la possibilité d'embarquer une ressource liée ;
- un format assez léger à produire et à lire.
Ce que ça n'apporte pas vraiment, en revanche, c'est une description riche de l'action métier elle-même. Le lien refund existe, mais le client doit encore savoir quelle méthode utiliser, quel payload envoyer, et quelle expérience construire autour.
HAL dit surtout : voilà où aller ensuite.
2. JSON:API
JSON:API structure beaucoup plus fortement les ressources. On y retrouve data, attributes, relationships, links, et éventuellement included pour éviter certains allers-retours.
{
"links": {
"self": "/orders/ORD-123"
},
"data": {
"type": "orders",
"id": "ORD-123",
"attributes": {
"status": "PAID",
"total": 249.00
},
"relationships": {
"customer": {
"links": {
"self": "/orders/ORD-123/relationships/customer",
"related": "/orders/ORD-123/customer"
},
"data": {
"type": "customers",
"id": "CUS-88"
}
}
}
},
"included": [
{
"type": "customers",
"id": "CUS-88",
"attributes": {
"name": "Acme Corp"
},
"links": {
"self": "/customers/CUS-88"
}
}
]
}Ce que ça apporte :
- une convention très claire pour les ressources et leurs relations ;
- une bonne cohérence sur des APIs riches en données ;
- une structure pratique quand on veut des collections, des inclusions et des relations bien tenues.
Mais il faut être clair sur un point : dans cet exemple, il ne manque rien du point de vue JSON:API.
L'absence d'un bloc d'actions n'est pas un oubli. C'est justement une limite intéressante du format pour le sujet qui nous occupe ici.
JSON:API est très bon pour dire :
- quelle est la ressource ;
- quels sont ses attributs ;
- quelles sont ses relations ;
- où retrouver les ressources liées.
En revanche, il est beaucoup moins naturel pour dire :
- quelles transitions métier sont possibles maintenant ;
- quelle action peut être déclenchée depuis cet état ;
- avec quelle méthode ;
- et avec quels champs attendus.
Autrement dit, JSON:API décrit très bien la structure de la donnée, mais beaucoup moins naturellement les affordances métier.
C'est important, parce que c'est précisément là que beaucoup d'équipes attendent quelque chose de l'hypermedia. Si ton besoin principal est d'exposer des actions comme approve, reject, reopen ou refund, JSON:API n'est pas le format le plus expressif par défaut.
On peut évidemment ajouter une convention maison, par exemple dans meta, pour exposer des actions disponibles. Mais à ce moment-là, on reste dans un projet réel tout à fait valable, pas dans l'usage le plus naturel ou le plus standard du format.
JSON:API dit surtout : voilà comment la donnée et ses relations sont structurées.
3. Siren
Siren est souvent le plus parlant quand on veut illustrer HATEOAS sur un vrai domaine métier. Il distingue properties, entities, links et surtout actions.
{
"class": ["order"],
"properties": {
"id": "ORD-123",
"status": "PAID",
"total": 249.00
},
"entities": [
{
"class": ["customer"],
"rel": ["customer"],
"href": "/customers/CUS-88"
}
],
"actions": [
{
"name": "refund",
"title": "Refund this order",
"method": "POST",
"href": "/orders/ORD-123/refunds",
"type": "application/json",
"fields": [
{ "name": "reason", "type": "text" },
{ "name": "amount", "type": "number" }
]
}
],
"links": [
{ "rel": ["self"], "href": "/orders/ORD-123" },
{ "rel": ["invoice"], "href": "/orders/ORD-123/invoice" }
]
}Ce que ça apporte :
- une séparation propre entre données, entités liées, liens et actions ;
- une manière explicite d'exposer une transition métier ;
- une description plus riche de l'action à exécuter ;
- un modèle mieux adapté à des workflows avec états, permissions et formulaires.
Siren dit non seulement où aller, mais aussi ce que tu peux faire maintenant et comment le faire.
Ce que ces trois exemples montrent vraiment
Sur le même cas métier :
- HAL est très bon pour rendre une API navigable avec un surcoût faible ;
- JSON:API est très bon pour structurer rigoureusement des ressources et leurs relations ;
- Siren est souvent le plus expressif quand l'enjeu principal est d'exposer des transitions métier.
Dit autrement :
- si tu veux surtout améliorer la navigation, HAL est un bon candidat ;
- si tu veux surtout standardiser la structure de tes ressources, JSON:API est très solide ;
- si tu veux porter des workflows dans l'API, Siren est souvent plus proche de ce que tu cherches.
Un exemple encore plus parlant : dossier métier avec validation
Prenons un dossier APP-42 en cours de revue.
Selon le rôle et l'état, on peut ou non :
- approuver ;
- rejeter ;
- demander des corrections ;
- rouvrir ;
- archiver.
C'est typiquement le genre de domaine où l'hypermedia devient intéressant.
HAL
{
"id": "APP-42",
"status": "IN_REVIEW",
"_links": {
"self": { "href": "/applications/APP-42" },
"approve": { "href": "/applications/APP-42/approve" },
"reject": { "href": "/applications/APP-42/reject" }
}
}JSON:API
{
"data": {
"type": "applications",
"id": "APP-42",
"attributes": {
"status": "IN_REVIEW"
},
"links": {
"self": "/applications/APP-42"
}
}
}Siren
{
"class": ["application"],
"properties": {
"id": "APP-42",
"status": "IN_REVIEW"
},
"actions": [
{
"name": "approve",
"method": "POST",
"href": "/applications/APP-42/approve"
},
{
"name": "reject",
"method": "POST",
"href": "/applications/APP-42/reject",
"fields": [
{ "name": "reason", "type": "text" }
]
}
],
"links": [
{ "rel": ["self"], "href": "/applications/APP-42" }
]
}La différence saute vite aux yeux.
Dans HAL, on voit les routes utiles. Dans JSON:API, on garde une représentation très propre de la ressource. Dans Siren, on voit déjà davantage le workflow.
Et c'est exactement là que le sujet devient intéressant pour un produit métier : l'API n'expose plus seulement un état, elle commence à exposer les capacités disponibles depuis cet état.
Faut-il choisir un seul hypermedia ou plusieurs ?
C'est une bonne question, parce qu'en théorie plusieurs formats peuvent cohabiter, mais en pratique ce n'est pas toujours une bonne idée.
Le point de départ est simple : une représentation donnée doit rester cohérente.
Autrement dit, une réponse ne devrait pas être à moitié en HAL, à moitié en Siren, avec un peu de conventions JSON:API au passage. À l'échelle d'une représentation, mélanger plusieurs dialectes revient surtout à produire un contrat confus.
En revanche, à l'échelle d'une API ou d'un système, plusieurs hypermedia peuvent cohabiter.
Par exemple :
- une surface historique exposée en HAL ;
- une API de ressources plus standardisée en JSON:API ;
- une partie plus orientée workflows métier en Siren.
Techniquement, ce n'est pas absurde. Mais il faut être clair sur le coût que ça introduit.
Dès que plusieurs formats cohabitent, on augmente :
- la complexité de documentation ;
- la charge mentale côté clients ;
- les conventions à maintenir ;
- les adaptations dans les SDK, fronts et tests ;
- et le risque de faire dériver la sémantique d'une zone à l'autre.
En pratique, il y a donc une règle simple que je trouve saine : un même type de clients a intérêt à parler principalement un seul dialecte hypermedia.
Si une même famille de fronts consomme la même API métier, choisir un format principal est souvent la meilleure option.
À l'inverse, la cohabitation de plusieurs hypermedia devient plus défendable quand il existe de vraies raisons d'intégration ou d'historique :
- clients très différents ;
- API déjà existantes qu'on ne veut pas casser ;
- besoin d'un format plus orienté ressources sur une zone, et plus orienté actions sur une autre.
Dit autrement : oui, plusieurs hypermedia peuvent cohabiter dans un même système. Mais cette cohabitation doit être délibérée, lisible et limitée.
Sinon, on ne gagne pas en souplesse. On gagne surtout en confusion.
Pourquoi c'est particulièrement pertinent sur des projets riches en règles métier
Prenons un exemple banal en apparence : un dossier de validation.
Il peut être :
- en brouillon ;
- soumis ;
- en revue ;
- validé ;
- rejeté ;
- rouvert ;
- archivé.
Et selon le rôle, on ne peut pas faire la même chose :
- le créateur peut modifier un brouillon ;
- le reviewer peut approuver ou rejeter ;
- l'administrateur peut rouvrir un dossier validé ;
- personne ne peut modifier un dossier archivé.
Dans beaucoup de systèmes, cette logique vit :
- dans le backend ;
- dans le front principal ;
- dans le backoffice ;
- parfois dans le mobile ;
- parfois dans un portail partenaire.
Résultat : la même règle métier se retrouve répétée sous plusieurs formes.
L'intérêt d'une approche hypermedia est de faire remonter la partie "transitions autorisées" au niveau de l'API.
Le client n'a plus seulement besoin de connaître le statut du dossier. Il reçoit aussi les actions disponibles pour ce dossier précis, dans ce contexte précis, pour cet utilisateur précis.
Concrètement, ça change beaucoup de choses.
Le bouton "Valider" n'est plus affiché parce qu'un développeur front a codé if status === REVIEW and role === REVIEWER dans trois applications différentes.
Il est affiché parce que l'API expose l'action approve ici et maintenant.
Ce n'est pas magique. Les règles existent toujours côté serveur.
Mais elles deviennent plus visibles, plus testables et moins dupliquées dans les clients.
L'intérêt quand plusieurs fronts consomment la même API
C'est probablement le cas d'usage le plus concret.
Dès qu'une même base métier alimente :
- un front web principal ;
- un backoffice interne ;
- une app mobile ;
- un portail partenaire ;
- parfois des scripts ou des intégrations externes ;
le coût de duplication de la logique de navigation augmente vite.
Sans hypermedia, chaque client embarque sa propre lecture de :
- quelles actions sont possibles ;
- quand elles le sont ;
- sur quelle route elles se trouvent ;
- avec quelle méthode ;
- avec quels paramètres.
Et même quand les équipes sont disciplinées, cette connaissance diverge avec le temps.
L'API métier devient alors une sorte de base commune… mais incomplète, parce qu'elle expose les données sans exposer clairement les transitions.
Avec hypermedia, on peut faire mieux.
L'API devient la source de vérité non seulement pour l'état, mais aussi pour les possibilités d'action. Chaque client garde sa liberté d'interface, de wording, d'ordre d'affichage et d'expérience utilisateur. En revanche, il dépend moins d'une copie locale du workflow.
C'est une nuance importante : HATEOAS ne remplace pas le design du front. Il réduit surtout le couplage métier implicite entre plusieurs fronts et une API.
Les avantages d'une vraie approche hypermedia
1. Moins de couplage sur les URI et les enchaînements
Quand un client suit des liens ou des actions fournies par l'API, il dépend moins d'URI codées en dur.
C'est utile dès qu'un système change de découpage interne, de nomenclature ou de stratégie d'exposition.
2. Des workflows plus explicites
L'API peut exprimer les transitions réellement possibles depuis un état donné.
On sort d'un modèle où le front "devine" ce qu'il a le droit de faire à partir d'une combinaison de statuts, de permissions et de conventions documentaires.
3. Une meilleure cohérence entre plusieurs clients
Quand plusieurs fronts consomment les mêmes représentations, ils ont plus de chances de rester alignés sur les mêmes règles de passage d'un état à un autre.
Ça n'élimine pas tout risque de divergence, mais ça le réduit.
4. Une API plus évolutive
Une API qui décrit mieux ses transitions laisse davantage de marge pour faire évoluer ses parcours sans casser immédiatement tous les consommateurs.
Ce n'est pas une immunité totale contre la casse. Mais c'est une meilleure base qu'une API qui exige de chaque client une connaissance détaillée et figée de tous les chemins possibles.
5. Un meilleur support des clients génériques ou semi-génériques
Dès qu'on veut construire :
- des consoles d'administration rapidement ;
- des explorateurs d'API ;
- des interfaces internes outillées ;
- des intégrations qui suivent des parcours assez normalisés ;
le fait que l'API expose ses liens et actions devient particulièrement utile.
Les inconvénients, qu'on sous-estime souvent
Il faut être honnête : HATEOAS est séduisant intellectuellement, mais son adoption a des coûts réels.
1. Le design est plus exigeant
Exposer des données est relativement simple.
Exposer proprement les transitions d'un système demande de clarifier :
- les états ;
- les permissions ;
- les relations ;
- la sémantique des actions ;
- et le contrat exact des représentations.
Autrement dit : HATEOAS révèle souvent la complexité réelle du domaine. Il ne la fait pas disparaître.
2. Le tooling est moins trivial que pour une API CRUD classique
Les écosystèmes savent très bien générer des clients sur des endpoints documentés de façon statique.
Ils sont souvent moins naturels dès qu'on veut piloter un client par hypermedia, surtout côté front moderne où beaucoup d'applications restent construites autour de fetchs ciblés et d'état local assez explicite.
3. Tous les fronts n'en tirent pas la même valeur
Un front très riche, très interactif, avec beaucoup de logique de présentation et de micro-interactions, ne devient pas automatiquement plus simple parce que l'API expose des actions hypermedia.
Le front gardera sa propre complexité d'interface.
L'intérêt se situe surtout sur la partie découverte des capacités métier et réduction de duplication des transitions.
4. La documentation ne disparaît pas
C'est un fantasme fréquent.
Une API hypermedia bien conçue reste plus auto-descriptive qu'une API purement documentaire. Mais elle ne supprime ni la documentation métier, ni les conventions de sécurité, ni les explications de domaine.
Elle réduit une partie de la connaissance "hors bande". Elle ne l'annule pas.
5. C'est parfois trop pour un produit simple
Si ton système est surtout un CRUD assez stable, avec un seul front, peu de règles de transition et peu de variation selon le contexte, HATEOAS peut être une sophistication inutile.
Dans ce cas, le coût de design, de représentation et d'appropriation peut dépasser le gain réel.
Le vrai bon cas d'usage
À mon sens, HATEOAS devient vraiment intéressant quand plusieurs conditions sont réunies :
- les états métier comptent autant que les données ;
- les transitions autorisées sont nombreuses ou mouvantes ;
- plusieurs clients doivent rester cohérents ;
- les permissions modifient fortement les parcours ;
- l'API n'est pas juste un transport, mais un support de workflow.
Dans ce cas, l'hypermedia permet de faire quelque chose de précieux : rapprocher la représentation API du comportement réel du système.
Et c'est souvent là que beaucoup d'architectures API sont faibles : elles exposent très bien la donnée, mais très mal l'usage autorisé de cette donnée.
Quand je ne pousserais pas HATEOAS
Je ne pousserais pas cette approche dans trois cas classiques.
Le premier : un produit simple, avec peu de transitions métier.
Le deuxième : une équipe qui n'a déjà pas stabilisé son modèle métier, ses ressources ou ses conventions HTTP de base. Dans ce cas, ajouter de l'hypermedia reviendra surtout à habiller une confusion existante.
Le troisième : des fronts qui continueront de toute façon à coder tout le workflow en dur, soit par habitude, soit parce que l'organisation ne veut pas investir dans un contrat hypermedia réellement utilisé.
Dans ces contextes, mieux vaut souvent une API claire, sobre et cohérente qu'une couche HATEOAS décorative que personne n'exploite vraiment.
Mon avis
HATEOAS souffre d'un double problème.
D'un côté, il est souvent présenté comme le test ultime de pureté REST, ce qui le rend vite abstrait ou dogmatique.
De l'autre, il est souvent réduit à trois liens ajoutés en sortie, ce qui le rend presque inutile.
Le sujet intéressant est entre les deux.
Une approche hypermedia bien pensée peut être très utile quand une API doit exprimer des parcours métier, pas seulement transporter des ressources.
Elle devient encore plus pertinente quand plusieurs interfaces doivent rester alignées sur les mêmes règles sans dupliquer partout la logique de transition.
Ce n'est pas la bonne réponse à tous les projets.
Mais sur un produit riche en workflows, en permissions, en états et en fronts multiples, c'est une piste beaucoup plus sérieuse qu'on ne le croit souvent.
Un exemple d'implémentation avec NestJS et Angular
Pour rendre cette idée plus concrète, j'ai publié un dépôt de démonstration qui applique ces principes sur une API NestJS et un front Angular :
github.com/Axons-dev/axons-hateoas
Le projet n'est pas seulement une démo front/back, il mets en oeuvre un ensemble de packages que j'ai développé pour simplifier la génération et l'exploitation du format d'hypermedia SIREN :
- une API NestJS qui expose des réponses
application/vnd.siren+json; - un client TypeScript HATEOAS indépendant du framework front ;
- une intégration Angular qui branche ce client dans l'injection de dépendances et expose un petit store basé sur les signals ;
- des exemples métier où les actions disponibles sont calculées côté API puis consommées côté front.
Le premier domaine de démo est volontairement proche des exemples de cet article : un workflow de validation de dossier. Un dossier peut être en brouillon, soumis, en revue, approuvé, rejeté, en correction ou archivé. Les actions comme approve, reject, request-changes, reopen ou archive ne sont pas devinées par Angular : elles apparaissent dans la représentation Siren uniquement quand la policy backend les autorise.
Le dépôt contient aussi une démo plus orientée réseau social, avec posts, utilisateurs et commentaires, pour montrer différentes approches.
Les packages npm publiés sont :
@axonsdev/hateoas-core, le cœur TypeScript indépendant de tout framework. Il définit le client, le modèle normalisé de ressource, les liens, les actions, les transports et les parsers.@axonsdev/hateoas-siren, le support du format Siren et le parser vers le modèle normalisé du core.@axonsdev/hateoas-fetch, le transport basé surfetch.@axonsdev/hateoas-angular, l'intégration Angular autour de@axonsdev/hateoas-core, avec provider DI et store de ressource.@axonsdev/hateoas-nestjs, les helpers NestJS pour composer des réponses Siren depuis des définitions de ressources, des liens, des profils et des actions.@axonsdev/hateoas-nestjs-typeorm, une intégration TypeORM assistée qui garde l'exposition des champs explicite pour éviter de publier accidentellement toute une entité.
L'intérêt de cette séparation est important : @axonsdev/hateoas-nestjs aide le backend à produire des représentations hypermedia, tandis que @axonsdev/hateoas-core et @axonsdev/hateoas-angular montrent comment les consommer sans remettre les règles de transition dans le front.
Un client peut alors écrire une intention de ce genre :
const resource = await client.get('/api/cases/CASE-001');
if (resource.hasAction('approve')) {
await resource.action('approve').submit();
}Ce code ne connaît pas la route métier POST /api/cases/:id/approve. Il suit l'action exposée par la représentation courante. C'est précisément le déplacement de responsabilité dont parle cet article : le front garde la responsabilité de l'expérience utilisateur, mais il ne recopie plus toute la matrice des transitions autorisées.
Conclusion
REST poussé jusqu'au bout ne consiste pas seulement à bien nommer ses routes et à utiliser GET, POST, PUT et DELETE correctement.
Le cœur du sujet, c'est aussi de savoir si l'API aide le client à comprendre ce qu'il peut faire maintenant.
C'est exactement ce que cherche HATEOAS.
Dans les systèmes simples, l'approche peut être superflue.
Dans les systèmes métier plus denses, elle peut au contraire devenir une vraie manière de :
- réduire le couplage implicite ;
- rendre les workflows plus explicites ;
- limiter la duplication des transitions dans plusieurs fronts ;
- et faire porter à l'API une part plus fidèle du comportement réel du produit.
Dit autrement : HATEOAS n'est pas juste un débat sur le "vrai REST".
C'est surtout une question d'architecture : où vit la connaissance des actions possibles dans ton système ?
Si la réponse actuelle est "un peu partout", alors le sujet mérite probablement d'être regardé de plus près.
Ce que cette approche soulève ensuite
Une fois qu'on a compris l'intérêt d'HATEOAS sur le papier, les vraies questions deviennent très concrètes.
La première, côté backend, est simple à formuler et moins simple à bien traiter : où vit la logique qui décide quelles actions exposer ?
Est-ce qu'on la met dans les entités, dans une couche d'assemblage de représentation, dans un orchestrateur métier, dans une policy, dans un state machine, dans un presenter dédié ? Et comment tester proprement ce contrat sans transformer la couche hypermedia en bricolage supplémentaire ?
La deuxième, côté front, est tout aussi intéressante : jusqu'où un client doit-il se laisser guider par l'API ?
Est-ce qu'un front React, Angular, Next.js ou un backoffice interne doit lire directement ces actions et construire l'interface à partir d'elles ? Ou vaut-il mieux garder une couche d'adaptation locale pour conserver une UI plus explicite et plus maîtrisée ?
C'est souvent là que le sujet devient réellement utile.
Tant qu'on reste au niveau des principes, HATEOAS peut sembler théorique. Dès qu'on regarde comment exposer ces représentations côté backend et comment les consommer proprement côté front, on entre dans des arbitrages d'architecture beaucoup plus concrets.
Ces deux questions méritent d'ailleurs d'être traitées séparément. Elles prolongent naturellement le sujet, parce qu'une fois l'intérêt de l'hypermedia compris, le vrai travail commence : le rendre maintenable côté API, puis réellement exploitable côté interface.
