03 - Les méthodes HTTP, pour de vrai
Ce que tu vas apprendre
- Les 7 méthodes HTTP utilisees en REST et leur semantique
- La différence entre méthode safe et idempotente
- PUT vs PATCH : le debat eternel, tranche une bonne fois
- Les erreurs les plus courantes avec les méthodes HTTP
Prerequisites
- Avoir lu l'article sur le design des URLs
- Comprendre les bases de HTTP (requête/réponse)
POST pour tout : le syndrome du marteau
Sur un vieux projet, j'ai découvert une API ou 100% des endpoints etaient en POST. Creer un utilisateur ? POST. Lire un utilisateur ? POST avec {"action": "read"} dans le body. Supprimer ? POST avec {"action": "delete"}. Le dev m'a dit : "POST, ca marche pour tout." Techniquement vrai. Semantiquement, c'est un massacre.
Les méthodes HTTP ne sont pas decoratives. Elles portent une semantique precise que les navigateurs, proxies, CDN et outils de monitoring comprennent. Utiliser la bonne méthode, c'est rendre ton API previsible.
Les méthodes, une par une
GET -- Lire une ressource
GET est safe (pas d'effet de bord) et idempotent (meme résultat a chaque appel). Pas de body dans la requête.
httpGET /api/users/42 HTTP/1.1
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json
{"id": 42, "name": "Alice", "email": "alice@example.com"}
POST -- Creer une ressource
POST n'est ni safe ni idempotent. Deux appels identiques creent deux ressources.
httpPOST /api/users HTTP/1.1
Content-Type: application/json
{"name": "Bob", "email": "bob@example.com"}
HTTP/1.1 201 Created
Location: /api/users/43
Content-Type: application/json
{"id": 43, "name": "Bob", "email": "bob@example.com"}
Note le header Location qui pointe vers la ressource créée. C'est un détail que beaucoup oublient mais qui rend l'API propre.
PUT -- Remplacer une ressource
PUT est idempotent. Tu envoies la representation complète de la ressource. Si tu oublies un champ, il est ecrase (souvent a null).
httpPUT /api/users/42 HTTP/1.1
Content-Type: application/json
{"name": "Alice Martin", "email": "alice.martin@example.com", "role": "admin"}
HTTP/1.1 200 OK
PATCH -- Modifier partiellement
PATCH n'est pas garanti idempotent (meme si en pratique, on l'implemente souvent de facon idempotente). Tu envoies seulement les champs a modifier.
httpPATCH /api/users/42 HTTP/1.1
Content-Type: application/json
{"email": "new-alice@example.com"}
HTTP/1.1 200 OK
DELETE -- Supprimer une ressource
DELETE est idempotent. Supprimer deux fois la meme ressource donne le meme résultat (la ressource n'existe plus).
httpDELETE /api/users/42 HTTP/1.1
HTTP/1.1 204 No Content
Un deuxieme appel peut renvoyer 204 ou 404 -- les deux sont valides. Mon opinion : 204 dans les deux cas, c'est plus simple pour le client.
HEAD -- GET sans le body
HEAD retourne les memes headers qu'un GET, mais sans body. Utile pour vérifier si une ressource existe ou pour checker les headers de cache.
httpHEAD /api/users/42 HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 89
ETag: "abc123"
OPTIONS -- Découvrir les méthodes disponibles
OPTIONS retourne les méthodes autorisees sur une URL. C'est aussi utilise par les navigateurs pour les requêtes CORS preflight.
httpOPTIONS /api/users HTTP/1.1
HTTP/1.1 204 No Content
Allow: GET, POST, HEAD, OPTIONS
Safe vs idempotent
Deux concepts que tout le monde melange :
| Méthode | Safe | Idempotent |
|---|---|---|
| GET | oui | oui |
| HEAD | oui | oui |
| OPTIONS | oui | oui |
| POST | non | non |
| PUT | non | oui |
| PATCH | non | non* |
| DELETE | non | oui |
Safe : la requête ne modifie pas l'état du serveur. Un GET ne doit jamais modifier de donnees. J'ai vu des GET qui declenchaient des effets de bord (envoi de mail, compteurs). Ne fais pas ca.
Idempotent : appeler N fois donne le meme résultat qu'appeler une fois. PUT et DELETE sont idempotents. POST ne l'est pas -- d'ou l'utilité des clés d'idempotence (on en parlera dans l'article sur les headers).
*PATCH n'est pas idempotent par spécification, mais en pratique on l'implemente souvent comme tel avec des "merge patch".
PUT vs PATCH : le vrai debat
En TypeScript, la différence est nette :
typescript// PUT : remplacement complet
router.put("/api/users/:id", async (req, res) => {
const user = await userService.replace(req.params.id, {
name: req.body.name, // obligatoire
email: req.body.email, // obligatoire
role: req.body.role, // obligatoire
bio: req.body.bio, // obligatoire, meme si vide
});
res.json(user);
});
// PATCH : mise a jour partielle
router.patch("/api/users/:id", async (req, res) => {
// seuls les champs presents sont mis a jour
const user = await userService.update(req.params.id, req.body);
res.json(user);
});
Mon avis apres des annees de pratique : utilise PATCH pour 90% de tes mises à jour. PUT, c'est bien pour les cas ou tu veux garantir que la ressource est dans un état complet et connu apres l'appel. Mais en réalité, les clients envoient rarement tous les champs.
Les erreurs classiques
- GET avec body : techniquement pas interdit par la spec, mais les proxies et CDN peuvent le supprimer. Ne fais pas ca.
- POST pour lire : ca casse le cache HTTP et empeche les navigateurs de fonctionner correctement.
- DELETE avec body : meme problème que GET avec body. Si tu as besoin de paramètres, utilise les query params.
- PUT partiel : si tu envoies 3 champs sur 10 avec PUT, les 7 autres seront null. Utilise PATCH.
Tu peux retrouver un recapitulatif de toutes les méthodes sur paltemps.fr.
Résumé
- Chaque méthode HTTP a une semantique precise : respecte-la
- GET est safe et idempotent, POST ne l'est ni l'un ni l'autre
- PUT remplace entièrement, PATCH modifie partiellement
- Prefere PATCH pour les mises à jour courantes
- HEAD et OPTIONS existent et sont utiles, ne les oublie pas
Precedent : Design des URLs | Suivant : Les codes de statut HTTP
Sources
- RFC 9110 -- HTTP Semantics, Section 9. https://www.rfc-editor.org/rfc/rfc9110#section-9
- RFC 5789 -- PATCH Method for HTTP. https://www.rfc-editor.org/rfc/rfc5789
- REST API Tutorial -- HTTP Methods. https://restfulapi.net/http-methods/