Chapitre 415 minIntermédiaire
Principes REST : ressources, URI et contraintes
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- énoncer les contraintes d'une architecture REST ;
- situer une API sur le modèle de maturité de Richardson ;
- nommer des URI de ressources selon les conventions ;
- exposer une action métier qui n'est pas du CRUD.
3 exercices corrigés1 schéma
REST (Representational State Transfer) est un style d'architecture pour les applications en réseau : le serveur expose des ressources identifiées par des URI, et le client les manipule en échangeant des représentations (un document JSON, par exemple) avec les méthodes standard de HTTP. Une API qui respecte ses contraintes est dite RESTful. Ce chapitre présente les six contraintes de Roy Fielding, le modèle de maturité de Richardson, les règles pour nommer les URI et la façon de traiter les actions qui ne sont pas de simples lectures ou écritures.
Ressource et représentation
Une ressource est tout ce qui mérite un nom dans ton application : un visiteur, une fiche de frais, la liste des fiches d'octobre, un justificatif, et même un concept comme « le profil de l'utilisateur connecté ». Elle a une identité stable, son URI : /v1/fiches/12.
Une représentation est l'état de la ressource à un instant donné, dans un format donné. La fiche 12 peut être représentée en JSON pour le front, en PDF pour l'impression, en CSV pour un export comptable. Le client ne manipule jamais la ressource elle-même (une ligne de la base, un objet en mémoire) : il reçoit une représentation avec GET, et en envoie une pour la créer ou la modifier avec POST, PUT ou PATCH. D'où le nom : transfert d'état par représentation.
Les six contraintes de Fielding
Dans sa thèse de 2000, Roy Fielding définit REST par six contraintes. Une API qui les respecte toutes gagne en simplicité, en performance et en capacité à grandir.
| Contrainte | Ce qu'elle impose | Ce qu'elle apporte |
|---|---|---|
| Client-serveur | L'interface (le client) est séparée du stockage et des traitements (le serveur) | Chaque côté évolue seul ; plusieurs clients partagent le même serveur |
| Sans état (stateless) | Chaque requête contient tout ce qu'il faut pour être comprise ; le serveur ne garde aucun contexte de session entre deux requêtes | N'importe quel serveur traite n'importe quelle requête : on ajoute des serveurs quand la charge monte |
| Mise en cache (cacheable) | Chaque réponse dit si elle peut être mise en cache, et combien de temps | Moins de requêtes, des réponses plus rapides |
| Interface uniforme | Toutes les ressources se manipulent de la même façon (voir ci-dessous) | Un développeur qui connaît une API REST comprend vite les autres |
| Système en couches | Le client ne sait pas s'il parle au serveur final ou à un intermédiaire (reverse proxy, cache, passerelle d'API, répartiteur de charge) | On ajoute de la sécurité, du cache ou de la répartition sans toucher au client |
| Code à la demande (facultative) | Le serveur peut envoyer du code exécutable au client (du JavaScript) | Rarement utilisée par les API |
L'interface uniforme est la contrainte qui distingue REST des autres styles. Elle se décompose en quatre règles :
- Identification des ressources : chaque ressource a son URI.
- Manipulation par les représentations : le client modifie une ressource en envoyant une représentation (le JSON de la fiche), pas en appelant une fonction.
- Messages autodescriptifs : chaque message dit comment le lire (la méthode, le
Content-Type, le code de statut), sans connaissance cachée. - L'hypermédia comme moteur de l'état de l'application (HATEOAS, Hypermedia As The Engine Of Application State) : les réponses contiennent des liens vers les actions possibles, comme une page web contient des liens.
Le modèle de maturité de Richardson
Leonard Richardson a proposé une échelle en quatre niveaux pour situer une API par rapport à REST. Elle est pratique pour analyser une API existante.
| Niveau | Ce que fait l'API | Exemple |
|---|---|---|
| 0 : le tunnel HTTP | Une seule URI, une seule méthode (POST) ; l'action est écrite dans le corps | POST /service avec {"action":"lireFiche","id":12} |
| 1 : les ressources | Une URI par ressource, mais toujours la même méthode | POST /fiches/12/lire, POST /fiches/12/supprimer |
| 2 : les méthodes HTTP | Les ressources et les méthodes et codes de statut de HTTP | GET /fiches/12 → 200 ; DELETE /fiches/12 → 204 |
| 3 : les contrôles hypermédia | Le niveau 2, plus des liens dans les réponses (HATEOAS) | La fiche renvoie un lien cloturer tant qu'elle est en cours de saisie |
La grande majorité des API professionnelles sont au niveau 2. C'est la cible de ton projet de BTS : des ressources bien nommées, les bonnes méthodes, les bons codes de statut.
Voici à quoi ressemble une réponse de niveau 3 : le serveur annonce les actions permises dans l'état actuel de la fiche, et le client n'a pas à coder la règle « on ne clôture qu'une fiche en cours ».
{
"id": 12,
"mois": "2026-10",
"etat": "CR",
"_links": {
"self": { "href": "/v1/fiches/12" },
"lignes-forfait": { "href": "/v1/fiches/12/lignes-forfait" },
"cloturer": { "href": "/v1/fiches/12", "method": "PATCH" }
}
}
Nommer les URI
Une URI bien choisie se lit comme une phrase et ne change jamais. Les conventions suivantes ne sont pas imposées par une norme, mais tu les retrouveras dans presque toutes les API sérieuses.
- Des noms, pas des verbes. Le verbe, c'est la méthode HTTP. On écrit
DELETE /v1/fiches/12, pasGET /v1/supprimerFiche?id=12. - Des noms au pluriel pour les collections, suivis de l'identifiant pour un élément :
/v1/ficheset/v1/fiches/12. Reste cohérent dans toute l'API. - Des minuscules et des tirets pour séparer les mots :
/v1/frais-hors-forfait, pas/v1/FraisHorsForfaitni/v1/frais_hors_forfait. Le chemin d'une URL est sensible à la casse. - La hiérarchie pour les relations de composition, sur deux niveaux au plus :
/v1/fiches/12/frais-hors-forfait(les frais de la fiche 12). Au-delà, l'URI devient fragile et longue. - La chaîne de requête pour filtrer, trier et paginer, pas le chemin :
/v1/fiches?etat=CL&mois=2026-10, pas/v1/fiches/cloturees/octobre. - Pas d'extension de fichier ni de détail technique : pas de
.php,.json, ni de nom de table. Le format se négocie avecAccept, et l'implémentation peut changer. - Pas de barre oblique finale :
/v1/fiches, pas/v1/fiches/. Choisis une forme et redirige ou refuse l'autre. - Des identifiants stables et non devinables si possible : l'identifiant d'une ressource ne doit pas changer, et un UUID (
/v1/justificatifs/3f1c9a2e-…) ne permet pas de deviner les ressources voisines comme un entier qui s'incrémente. Ce n'est pas une protection suffisante, mais une défense complémentaire.
| À éviter | Pourquoi | À écrire |
|---|---|---|
GET /v1/getFiches | Verbe dans l'URI | GET /v1/fiches |
POST /v1/fiches/creer | Verbe ; POST sur la collection suffit | POST /v1/fiches |
GET /v1/fiche/12 puis GET /v1/fiches | Singulier et pluriel mélangés | GET /v1/fiches/12 |
GET /v1/fiches.php?id=12 | Extension et technologie exposées | GET /v1/fiches/12 |
GET /v1/Visiteurs/7/FichesFrais | Majuscules, casse incohérente | GET /v1/visiteurs/7/fiches |
GET /v1/fiches/etat/CL/mois/2026-10 | Des filtres déguisés en hiérarchie | GET /v1/fiches?etat=CL&mois=2026-10 |
GET /v1/visiteurs/7/fiches/12/frais-hors-forfait/58/justificatif | Hiérarchie trop profonde | GET /v1/frais-hors-forfait/58/justificatif |
Collection et élément : le tableau CRUD
Chaque ressource s'expose en général sous deux formes : la collection (/v1/fiches) et l'élément (/v1/fiches/12). Les opérations CRUD (Create, Read, Update, Delete) se répartissent ainsi :
| Opération | Méthode et URI | Code de succès | SQL équivalent |
|---|---|---|---|
| Lister (avec filtres) | GET /v1/fiches?etat=CL | 200 | SELECT … WHERE etat = 'CL' |
| Lire un élément | GET /v1/fiches/12 | 200 | SELECT … WHERE id = 12 |
| Créer | POST /v1/fiches | 201 + Location | INSERT |
| Remplacer | PUT /v1/fiches/12 | 200 ou 204 | UPDATE de toutes les colonnes |
| Modifier en partie | PATCH /v1/fiches/12 | 200 ou 204 | UPDATE de quelques colonnes |
| Supprimer | DELETE /v1/fiches/12 | 204 | DELETE |
Les méthodes qui n'ont pas de sens sur une URI renvoient 405 : on ne fait ni PUT ni DELETE sur toute la collection /v1/fiches, ni POST sur un élément /v1/fiches/12 (sauf si l'élément contient lui-même une collection).
Les ressources d'AppliFrais
Partons du besoin. Un visiteur crée une fiche par mois, y saisit des frais forfaitisés (étapes, kilomètres, nuitées, repas) et des frais hors forfait avec leur justificatif, puis la clôture. Un comptable consulte les fiches clôturées, les valide et les met en paiement. Les ressources qui en découlent :
| Ressource | URI | Méthodes |
|---|---|---|
| Visiteurs (gérés par l'administrateur) | /v1/visiteurs, /v1/visiteurs/{id} | GET, POST ; GET, PATCH, DELETE |
| Profil de l'utilisateur connecté | /v1/moi | GET, PATCH |
| Fiches de frais | /v1/fiches, /v1/fiches/{id} | GET, POST ; GET, PATCH, DELETE |
| Frais forfaitisés d'une fiche | /v1/fiches/{id}/lignes-forfait | GET, PUT (on remplace les quatre quantités d'un coup) |
| Frais hors forfait d'une fiche | /v1/fiches/{id}/frais-hors-forfait | GET, POST |
| Un frais hors forfait | /v1/frais-hors-forfait/{id} | GET, PATCH, DELETE |
| Justificatif d'un frais | /v1/frais-hors-forfait/{id}/justificatif | GET, PUT, DELETE |
Remarque le choix pour les frais hors forfait : on les crée sous leur fiche (POST /v1/fiches/12/frais-hors-forfait, la relation est claire), mais on les manipule ensuite par leur propre URI, plus courte. Les deux formes coexistent souvent.
Les actions qui ne sont pas du CRUD
Clôturer une fiche, la valider, la mettre en paiement : ce ne sont ni des créations, ni des suppressions. Trois façons de les exposer, de la plus « REST » à la plus pragmatique.
Modifier un attribut d'état. La clôture est un changement de l'attribut etat :
PATCH /v1/fiches/12 HTTP/1.1
Content-Type: application/json
{"etat":"CL"}
L'API vérifie que la transition est permise (CR vers CL, CL vers VA, VA vers MP) et renvoie 409 sinon. C'est simple, mais une seule route porte des règles de droits différentes selon la valeur envoyée : le visiteur peut clôturer, seul le comptable peut valider.
Créer une ressource qui représente l'action. Une validation est un événement qui a une date, un auteur, un montant validé : elle mérite d'être une ressource.
POST /v1/fiches/12/validations HTTP/1.1
Content-Type: application/json
{"montantValide": 389.20, "commentaire": "Repas du 14 refusé : justificatif illisible"}
L'API crée la validation (201), passe la fiche à l'état VA et garde une trace de qui a validé quoi. Les droits sont simples à poser : seuls les comptables peuvent faire POST sur /validations.
Une sous-ressource d'action, avec un verbe. POST /v1/fiches/12/cloture. Ce n'est pas pur, mais c'est lisible, et de nombreuses API publiques le font (GitHub, Stripe). Si tu choisis cette forme, garde POST et réserve-la aux vraies actions métier.
REST face au style RPC
Le style RPC (Remote Procedure Call, appel de procédure distante) expose des fonctions plutôt que des ressources : POST /lireFiche, POST /validerFiche. SOAP, gRPC et JSON-RPC en sont des exemples. Ce n'est pas une erreur en soi : gRPC est un excellent choix entre microservices (chapitre 12). Mais une API qui mélange les deux styles sans le dire est déroutante.
| Style REST | Style RPC | |
|---|---|---|
| Ce qu'on expose | Des ressources (des noms) | Des procédures (des verbes) |
| L'action | Dans la méthode HTTP | Dans le nom de la procédure |
| Les erreurs | Dans le code de statut HTTP | Souvent dans le corps, avec un 200 |
| Le cache HTTP | Fonctionne naturellement sur les GET | Inutilisable si tout passe par POST |
| Exemple | GET /v1/fiches/12 | POST /rpc avec {"method":"lireFiche","params":{"id":12}} |
Exercices
Exercice 1 sur 3 Corriger des URI Réussi À revoir
Réécris chaque route dans le style REST (méthode et URI), en conservant le sens.
GET /api/getAllVisiteursPOST /api/visiteur/delete?id=7GET /api/fiches/visiteur/7/mois/2026-10POST /api/updateFiche.phpavec{"id":12,"etat":"CL"}GET /api/Fiches/12/LignesForfaitGET /api/rechercherFiches?montantMin=500
Voir la réponseMasquer la réponse de l'exercice 1
GET /v1/visiteursDELETE /v1/visiteurs/7GET /v1/fiches?visiteur=7&mois=2026-10: le visiteur et le mois sont des filtres.GET /v1/visiteurs/7/fiches?mois=2026-10est aussi acceptable.PATCH /v1/fiches/12avec{"etat":"CL"}GET /v1/fiches/12/lignes-forfaitGET /v1/fiches?montantMin=500: une recherche est une lecture filtrée de la collection.
Tu avais trouvé ?
Exercice 2 sur 3 Situer une API sur l'échelle de Richardson Réussi À revoir
Donne le niveau de maturité de chaque API et justifie.
- Toutes les requêtes passent par
POST /api.phpavec un champ"action"dans le corps ; la réponse est toujours200, avec un champ"succes": trueoufalse. - L'API expose
/clients,/clients/5,/commandes; tout se fait enGET(y comprisGET /clients/5/supprimer). - L'API utilise
GET,POST,PATCHetDELETEsur des ressources, et renvoie201,404,409à bon escient. - Comme la 3, et chaque commande renvoie des liens
annuleroupayerselon son état.
Voir la réponseMasquer la réponse de l'exercice 2
- Niveau 0 : une seule URI, une seule méthode, les erreurs cachées dans le corps.
- Niveau 1 : des ressources identifiées, mais pas les méthodes HTTP. Et
GET /clients/5/supprimerest dangereux, carGETdoit être sûr : un robot qui suit le lien supprime le client. - Niveau 2 : ressources, méthodes et codes de statut. C'est le niveau visé par la plupart des API.
- Niveau 3 : les contrôles hypermédia (HATEOAS) guident le client.
Tu avais trouvé ?
Exercice 3 sur 3 Concevoir les ressources d'un cas Réussi À revoir
La médiathèque d'une commune veut une API pour son application mobile. Un adhérent consulte le catalogue (livres, DVD), réserve un document, voit ses prêts en cours et prolonge un prêt une fois. Un bibliothécaire enregistre les prêts et les retours.
- Liste les ressources et leurs URI.
- Donne la méthode et l'URI pour : chercher les documents dont le titre contient « Dune » ; réserver le document 314 ; voir mes prêts en cours ; prolonger le prêt 88 ; enregistrer le retour du prêt 88.
- Quel code renvoyer si l'adhérent prolonge une deuxième fois le même prêt ?
Voir la réponseMasquer la réponse de l'exercice 3
/v1/documentset/v1/documents/{id}(le catalogue) ;/v1/adherents/{id}ou/v1/moi(le profil) ;/v1/reservationset/v1/reservations/{id};/v1/pretset/v1/prets/{id}.- Une proposition :
- recherche :
GET /v1/documents?titre=Dune - réservation :
POST /v1/reservationsavec{"documentId":314}(l'adhérent est celui du jeton) - prêts en cours :
GET /v1/moi/prets?statut=en-cours, ouGET /v1/prets?statut=en-cours, l'API ne renvoyant que les prêts de l'adhérent connecté - prolongation :
POST /v1/prets/88/prolongations(une ressource qui garde la trace), ouPATCH /v1/prets/88avec la nouvelle date de retour - retour :
PATCH /v1/prets/88avec{"dateRetour":"2026-11-02"}, ouPOST /v1/prets/88/retour, réservé au rôle bibliothécaire
- recherche :
409 Conflict: la demande est bien formée, mais contredit l'état du prêt (déjà prolongé une fois). Le corps de l'erreur explique la règle.
Tu avais trouvé ?