đ API REST â Le Guide Ultime
Deep Dive : Principes (Stateless), Verbes HTTP (CRUD), Status Codes, JSON & Tooling (OpenAPI).
1. C'est quoi REST ?
REpresentational State Transfer. Un *style* d'architecture (pas un protocole). RPC vs REST.
REST Architecture2. Les 6 Contraintes
Client-Serveur, Stateless (la plus importante), Cacheable, Uniform Interface.
Stateless Constraints3. REST vs SOAP
La "guerre" des APIs. Style vs Protocole, Léger (JSON) vs Lourd (XML Envelope).
SOAP XML-RPC4. REST vs GraphQL
Le "nouveau" challenger. Multi-Endpoints vs Single-Endpoint. Over/Under-fetching.
GraphQL Overfetching5. Ressources & URIs
Les "Noms" de l'API. Bon vs Mauvais design. /users/123/posts. Query params (?).
6. âïž Verbes HTTP (CRUD)
Les "Verbes" de l'API. GET, POST, PUT, PATCH, DELETE. Le mapping CRUD.
7. đŠ Codes de Statut HTTP
La réponse. 2xx (Success), 4xx (Client Error), 5xx (Server Error). 200, 201, 404.
8. Le format : JSON
La "Représentation". Agnostique (XML/YAML), mais 99% JSON. Content-Type.
9. "Stateless" (Sans état)
La contrainte la plus importante. Session (Stateful) vs Token (Stateless). JWT.
Stateless JWT10. Idempotence
GET/PUT/DELETE vs POST. L'importance pour les "retries" (ré-essais).
11. Versioning
Gérer les "breaking changes". /v1/ vs Header (Accept). L'approche "additive".
12. Outils & HATEOAS
Postman, OpenAPI (Swagger), HATEOAS (le "H" de REST). Liens utiles.
OpenAPI PostmanREST signifie REpresentational State Transfer. C'est un style d'architecture (un ensemble de rÚgles/principes), *pas* un protocole ou un standard (contrairement à SOAP). Il a été défini par Roy Fielding dans sa thÚse en 2000.
L'idée de base est de traiter le serveur comme une collection de "Ressources" (des "Noms", ex: un utilisateur, un produit) que l'on peut manipuler en utilisant les verbes HTTP (des "Verbes", ex: GET, POST) standards.
RPC vs REST (Le changement de mentalité)
Avant REST, les APIs étaient souvent "RPC" (Remote Procedure Call). On appelait des *fonctions* sur le serveur.
RPC (Ancienne méthode)
L'URL décrit une *action*. (Verbes dans l'URL).
POST /api/getUser.php?id=123 POST /api/createNewUser POST /api/updateUser?id=123
REST (Méthode moderne)
L'URL décrit une *ressource*. L'action est dans le verbe HTTP.
GET /api/users/123 POST /api/users PUT /api/users/123
Une API "RESTful" est une API qui respecte les 6 contraintes de REST (voir 2.1).
Une API n'est "RESTful" que si elle respecte 6 contraintes architecturales. (En pratique, beaucoup d'APIs "REST" ne respectent pas la #4 et la #6).
| Contrainte | Description (Ce que ça implique) |
|---|---|
| 1. Client-Serveur | Séparation claire des responsabilités. L'UI (client) ne connaßt pas la BDD. Le Serveur (API) ne connaßt pas l'UI. Permet aux deux d'évoluer indépendamment. |
| 2. Stateless (Sans Ătat) | La plus importante (voir 9.1). Chaque requĂȘte du client doit contenir *toute* l'information nĂ©cessaire pour ĂȘtre comprise. Le serveur ne stocke *aucun* Ă©tat de session (ex: $_SESSION) entre les appels. L'authentification est envoyĂ©e Ă *chaque* appel (ex: Token JWT). |
| 3. Cacheable (Mise en cache) | Les rĂ©ponses du serveur doivent (si possible) ĂȘtre marquĂ©es comme "cacheables" ou "non-cacheables". Cela permet au client (ou Ă un proxy) de rĂ©utiliser d'anciennes rĂ©ponses pour amĂ©liorer la performance (ex: Cache-Control, ETag). |
| 4. Uniform Interface (Interface Uniforme) | C'est le "cĆur" de REST, dĂ©composĂ© en 4 sous-contraintes : a. Identification des ressources (via les URIs, ex: /users/123). b. Manipulation via ReprĂ©sentations (le client modifie un JSON, pas l'objet BDD). c. Messages Auto-descriptifs (ex: Content-Type: application/json). d. HATEOAS (voir 12.1). |
| 5. Layered System (SystÚme en Couches) | Le client ne sait pas s'il parle au serveur final, à un proxy, ou à un load balancer. Cela permet d'insérer des couches (cache, sécurité) sans que le client ne le sache. |
| 6. Code-On-Demand (Optionnelle) | (La moins utilisée). Le serveur peut (rarement) envoyer du code exécutable (ex: du JavaScript) au client pour étendre ses fonctionnalités. |
Avant REST, SOAP (Simple Object Access Protocol) était le standard (surtout dans le monde Java et .NET). C'est un *protocole* lourd et rigide.
| CritĂšre | API REST | API SOAP |
|---|---|---|
| Type | Style d'architecture (un guide) | Protocole (un standard strict) |
| Transport | Utilise HTTP (GET, POST...) | Agnostique (peut utiliser HTTP, SMTP, ...). |
| Format de Données | Agnostique (mais 99% JSON) | XML uniquement (dans une enveloppe <soap:Envelope>) |
| Contrat | Optionnel (OpenAPI/Swagger) | Obligatoire (WSDL). Un fichier XML complexe qui décrit l'API. |
| LégÚreté | TrÚs léger (ex: { "nom": "Jean" }) | TrÚs lourd (beaucoup de XML de "plomberie"). |
| Standards (WS-*) | Aucun (géré par HTTP) | Supporte WS-Security, WS-Transactions... (trÚs robuste pour les banques). |
RequĂȘte REST (CrĂ©er un user)
POST /users
Content-Type: application/json
{
"name": "Jean"
}
RequĂȘte SOAP (CrĂ©er un user)
POST /UserService
Content-Type: application/soap+xml
<?xml version="1.0"?>
<soap:Envelope ...>
<soap:Body>
<m:CreateUser>
<m:Name>Jean</m:Name>
</m:CreateUser>
</soap:Body>
</soap:Envelope>
GraphQL est le "challenger" moderne de REST. Il ne remplace pas REST, mais résout certains de ses problÚmes, notamment le "Over/Under-fetching".
| CritĂšre | API REST | API GraphQL |
|---|---|---|
| Endpoints | Multiple Endpoints (/users, /posts) | Single Endpoint (/graphql) |
| "Forme" de la DonnĂ©e | DictĂ©e par le Serveur. | DictĂ©e par le Client (via la requĂȘte). |
| Over-fetching | Oui. GET /users/1 renvoie *tout* (email, adresse) mĂȘme si je ne veux que le nom. | Non. Le client demande { user(id: "1") { name } }. |
| Under-fetching | Oui. GET /posts/1 ne renvoie pas l'auteur. Il faut un 2e appel GET /users/123. | Non. Le client demande { post(id: "1") { title, author { name } } }. |
| Contrat | Optionnel (OpenAPI) | Obligatoire (Schéma SDL). Typage fort. |
Exemple : Le ProblĂšme du Dashboard
Objectif : Afficher le nom de l'utilisateur, et les titres de ses 3 derniers articles.
REST (Multiple Endpoints)
Le client doit faire 2 appels (ou plus) et assembler les données.
/* Appel 1: */
GET /api/me
<-- { "id": 1, "name": "Jean", "email": "..." }
/* Appel 2 (aprĂšs avoir eu l'ID): */
GET /api/users/1/posts?limit=3
<-- [
{ "id": 10, "title": "Titre 1", ... },
{ "id": 11, "title": "Titre 2", ... }
]
GraphQL (Single Endpoint)
Le client envoie une seule requĂȘte qui dĂ©crit ses besoins.
/* Appel 1 (POST /graphql): */
query {
me {
name
posts(limit: 3) {
title
}
}
}
/* Réponse unique : */
<-- {
"data": {
"me": {
"name": "Jean",
"posts": [ { "title": "Titre 1" }, ... ]
}
}
}
En REST, les URIs (Uniform Resource Identifiers) identifient les **Ressources** (les "Noms"). Elles ne doivent **jamais** contenir de verbes (actions).
Bon vs Mauvais Design d'URI
| Mauvais (RPC) | Bon (REST) | Description |
|---|---|---|
/getUsers | GET /users | L'action est dans le verbe (GET). |
/createUser | POST /users | L'action est dans le verbe (POST). |
/users/123/update | PUT /users/123 | L'action est dans le verbe (PUT). |
/users/123/delete | DELETE /users/123 | L'action est dans le verbe (DELETE). |
Collections vs ĂlĂ©ments
- Collection : (ex:
/posts) - La liste de *tous* les articles. - ĂlĂ©ment : (ex:
/posts/123) - Un article *spécifique*.
Ressources Imbriquées (Nested)
Pour exprimer des relations (Parent/Enfant).
/* Récupérer TOUS les commentaires */ GET /comments /* Récupérer TOUS les commentaires de l'article 123 */ GET /posts/123/comments /* Récupérer LE commentaire 456 de l'article 123 */ GET /posts/123/comments/456
Filtrage (Query Parameters)
Pour filtrer, trier ou paginer une *collection*, on utilise les "query string params" (?).
/* Récupérer les articles publiés, triés par date */ GET /posts?status=published&sort=date_desc /* Récupérer la page 2 (10 articles par page) */ GET /posts?page=2&limit=10
Les verbes HTTP sont les "actions" que l'on applique aux "ressources" (URIs). Le mapping CRUD (Create, Read, Update, Delete) est le fondement de REST.
Les 5 Verbes Principaux
| Verbe | Action | Ressource (URI) | Description (CRUD) |
|---|---|---|---|
| GET | Lire une ressource. | /users ou /users/123 | Read. Sûr (Safe) et Idempotent. (Ne modifie rien). |
| POST | Créer une nouvelle ressource. | /users | Create. Non-Idempotent. (Appeler 2x crée 2 users). |
| PUT | Remplacer une ressource existante. | /users/123 | Update (total). Idempotent. (Doit envoyer l'objet *entier*). |
| PATCH | Modifier partiellement une ressource. | /users/123 | Update (partiel). (N'envoie que le champ Ă changer, ex: { "email": "..." }). |
| DELETE | Supprimer une ressource. | /users/123 | Delete. Idempotent. |
PUT vs PATCH (Une distinction importante)
PUT /users/123 (Remplacement)
Doit ĂȘtre idempotent. Remplace l'objet entier.
/* Donnée actuelle sur le serveur:
{ "id": 123, "name": "Jean", "job": "Dev" }
*/
/* RequĂȘte (on oublie "job"): */
PUT /users/123
{
"name": "Nouveau Jean"
}
/* Résultat (le "job" est effacé !):
{ "id": 123, "name": "Nouveau Jean" }
*/
PATCH /users/123 (Mise Ă jour partielle)
Modifie *uniquement* les champs fournis.
/* Donnée actuelle sur le serveur:
{ "id": 123, "name": "Jean", "job": "Dev" }
*/
/* RequĂȘte (on ne change que "name"): */
PATCH /users/123
{
"name": "Nouveau Jean"
}
/* Résultat (le "job" est préservé):
{ "id": 123, "name": "Nouveau Jean", "job": "Dev" }
*/
Le client (navigateur, app) a besoin de savoir si l'opération a réussi, échoué, ou pourquoi. Le code de statut HTTP est la réponse standardisée du serveur.
Les 5 Classes de Codes
1xx(Information) : "J'ai reçu, je continue..." (Rare en REST).2xx(SuccÚs) : "OK, j'ai fait ce que tu as demandé."3xx(Redirection) : "Va voir ailleurs."4xx(Erreur Client) : "Tu as fait une erreur." (ex: 404 Not Found)5xx(Erreur Serveur) : "J'ai fait une erreur." (ex: 500 Internal Error)
Les Codes Incontournables
| Code | Signification | Quand l'utiliser ? |
|---|---|---|
200 OK | SuccÚs. | Réponse à un GET réussi. Réponse à un PUT/PATCH réussi. |
201 Created | Ressource Créée. | Réponse à un POST qui a créé une nouvelle ressource. (Souvent renvoie l'URL de la ressource dans le header Location). |
204 No Content | SuccÚs (Sans Contenu). | Réponse à un DELETE réussi. (Le body de la réponse est vide). |
304 Not Modified | Non Modifié. | (Pour le cache) Réponse à un GET si le client a déjà la derniÚre version. |
400 Bad Request | Mauvaise RequĂȘte. | Erreur de validation. (Ex: POST /users sans le champ email obligatoire). |
401 Unauthorized | Non Autorisé (Authentification). | "Je ne sais pas qui tu es." (Token manquant ou invalide). |
403 Forbidden | Interdit (Permissions). | "Je sais qui tu es, mais tu n'as pas le droit de faire ça." |
404 Not Found | Non Trouvé. | La ressource (ex: /users/999) n'existe pas. |
500 Internal Server Error | Erreur Interne du Serveur. | L'API a planté (ex: Erreur BDD, NullPointerException). |
Le "R" de REST (Representational) signifie que le client n'interagit jamais avec la "vraie" ressource (ex: la ligne en BDD), mais avec une représentation de celle-ci (JSON, XML, ...).
Bien que REST soit agnostique (il peut utiliser XML, YAML, ou mĂȘme du texte), le JSON (JavaScript Object Notation) est devenu le standard *de facto* pour 99% des APIs REST.
Pourquoi JSON ?
- Léger : Beaucoup moins verbeux que le XML (pas de balises fermantes).
- Natif pour JavaScript : Le format JSON est la syntaxe littérale d'un objet JavaScript.
JSON.parse()etJSON.stringify()sont tout ce dont on a besoin. - Lisible par l'humain : Facile à lire et à débugger.
L'importance du Content-Type
Les messages REST doivent ĂȘtre "auto-descriptifs". L'en-tĂȘte (header) HTTP Content-Type est crucial.
Exemple : RequĂȘte (Client -> Serveur)
POST /api/users
Host: api.example.com
Content-Type: application/json /* <-- Je préviens le serveur: "Je t'envoie du JSON" */
Accept: application/json /* <-- Je préviens le serveur: "J'aimerais une réponse en JSON" */
{
"username": "jean",
"email": "jean@mail.com"
}
Exemple : Réponse (Serveur -> Client)
HTTP/1.1 201 Created
Content-Type: application/json /* <-- Le serveur confirme: "Je te réponds en JSON" */
Location: /api/users/124
{
"id": 124,
"username": "jean",
"email": "jean@mail.com",
"createdAt": "2025-11-06T16:30:00Z"
}
C'est la contrainte la plus importante et la plus structurante de REST. "Stateless" = Sans état.
Cela signifie que le **serveur** n'a pas le droit de stocker d'information sur la "session" du client. Chaque requĂȘte du client doit contenir *toute* l'information (y compris l'authentification) pour que le serveur puisse la traiter de maniĂšre autonome.
Ancienne méthode : STATEFUL (Avec état)
L'architecture "classique" (ex: PHP/JSP).
- Client :
POST /login(user/pass). - Serveur : Vérifie. Crée une Session en RAM (
$_SESSION['user_id'] = 123). - Serveur : Envoie un Cookie (
session_id=abc). - Client :
GET /me(envoiesession_id=abc). - Serveur : Lit le cookie, trouve la session
abcen RAM, voit l'ID 123.
ProblÚme : Scalabilité. Si j'ai 10 serveurs (Load Balancer), l'utilisateur *doit* retomber sur le serveur qui détient sa session (sticky session), sinon il est déconnecté.
Méthode REST : STATELESS (Sans état)
L'architecture "moderne" (Tokens).
- Client :
POST /token(user/pass). - Serveur : Vérifie. Crée un Token JWT auto-suffisant (chiffré). Ne stocke RIEN.
- Serveur : Envoie le Token (
{ "token": "ey..." }). - Client :
GET /me(envoie le HeaderAuthorization: Bearer ey...). - Serveur : Lit le Header, décode le JWT (vérifie la signature), voit l'ID 123.
Avantage : ScalabilitĂ© infinie. N'importe lequel de mes 100 serveurs peut traiter la requĂȘte, car le Token contient *tout* l'Ă©tat.
Une opĂ©ration (un verbe HTTP) est idempotente si l'appeler une fois a *exactement* le mĂȘme effet sur l'Ă©tat du serveur que l'appeler 100 fois.
Pourquoi c'est important ? Les "retries" (rĂ©-essais). Si une app mobile envoie une requĂȘte et subit une coupure rĂ©seau (elle ne reçoit jamais la rĂ©ponse), peut-elle renvoyer la requĂȘte sans risque ?
| Verbe | Idempotent ? | Explication |
|---|---|---|
GET /users/1 | â Oui | Vous obtiendrez 100 fois la mĂȘme rĂ©ponse (ou un 404). L'Ă©tat du serveur ne change pas. |
POST /users | â Non | Si vous envoyez 100 fois la requĂȘte de crĂ©ation, vous allez crĂ©er 100 nouveaux utilisateurs. C'est dangereux. |
PUT /users/1 | â Oui | Vous remplacez l'utilisateur 1 par le mĂȘme objet 100 fois. L'Ă©tat final est identique au premier appel. |
DELETE /users/1 | â Oui | Le 1er appel supprime l'utilisateur. Les 99 appels suivants renvoient "404 Not Found". L'Ă©tat final du systĂšme est le mĂȘme : l'utilisateur 1 est supprimĂ©. |
PATCH /users/1 | â ïž Non (Techniquement) | Ăa dĂ©pend. Si PATCH { "name": "Jean" }, c'est idempotent. Mais si PATCH { "action": "increment_visits" }, ce n'est pas idempotent. Par prĂ©caution, on le considĂšre non-idempotent. |
Le ProblÚme : Votre API v1 est utilisée par une app mobile. Vous devez faire un "breaking change" (ex: renommer le champ "name" en "firstName" et "lastName"). Comment faire sans casser l'ancienne app mobile (v1) ?
Vous devez introduire une v2. Il y a 3 façons de faire.
| Méthode | Exemple | Avantages | Inconvénients |
|---|---|---|---|
| 1. Versioning par URI | /api/v1/users/api/v2/users | Le plus simple. Clair, facile à tester (on voit la version dans l'URL). | "Pollue" l'URI (qui est censée identifier une ressource, pas sa version). |
| 2. Versioning par Header | Accept: application/vnd.myapi.v1+jsonAccept: application/vnd.myapi.v2+json | Le plus "pur" (REST). L'URI (/users) ne change jamais. | Complexe. Difficile Ă tester (on ne peut pas coller l'URL dans un navigateur). |
| 3. Versioning par Query Param | /api/users?version=1/api/users?version=2 | Facile Ă tester. | Moyen. Les "query params" sont pour le *filtrage*, pas pour la *version*. |
L'Approche "Additive" (No-Version)
La meilleure solution est... de ne pas versionner.
- Ne jamais faire de "Breaking Change".
- (v1) Réponse :
{ "name": "Jean Dupont" }. - (v2) Vous voulez changer ? Ajoutez des champs, ne supprimez/renommez rien.
- (v2) Réponse :
{ "name": "Jean Dupont", "firstName": "Jean", "lastName": "Dupont" }. - L'ancienne app (v1) ne lira que
name. La nouvelle app (v2) lirafirstNameetlastName. Tout le monde est content.
1. HATEOAS (Le "H" de REST)
Hypermedia As The Engine Of Application State. C'est la contrainte la plus pure de REST, et celle que 99% des APIs ignorent.
L'idée : Le client ne devrait *jamais* avoir à "construire" des URLs. La réponse de l'API doit contenir les *actions* (liens) possibles.
Exemple : GET /users/123 (avec HATEOAS)
{
"id": 123,
"name": "Jean",
"job": "Dev",
/* HATEOAS: Le serveur dit au client ce qu'il peut faire ensuite */
"_links": {
"self": {
"href": "/users/123",
"method": "GET"
},
"posts": {
"href": "/users/123/posts",
"method": "GET"
},
"update": {
"href": "/users/123",
"method": "PUT"
},
"delete": {
"href": "/users/123",
"method": "DELETE"
}
}
}
2. Outils (L'écosystÚme)
| Outil | Description |
|---|---|
| Postman / Insomnia | Des clients GUI (Interfaces graphiques) pour tester vos requĂȘtes (GET, POST...) sans Ă©crire de code. Indispensable. |
| OpenAPI (ex-Swagger) | Une spécification (format YAML/JSON) pour *décrire* votre API : quels endpoints, quels verbes, quels paramÚtres, quelles réponses. C'est le "contrat" (le XSD de REST). |
| Swagger UI | Un outil qui lit votre fichier OpenAPI (openapi.json) et gĂ©nĂšre une **documentation web interactive** magnifique, oĂč l'on peut tester les endpoints. |
