Conception d’URL orientées ressources : collections et éléments
Un endpoint REST lisible décrit une ressource via un chemin stable, et une action via le verbe HTTP. On distingue généralement :
- Collection : représente un ensemble d’éléments (ex.
/users). - Élément : représente une instance unique identifiée (ex.
/users/{id}).
Règles pratiques de base
- Utiliser des noms (pas des verbes) dans les chemins :
/usersplutôt que/getUsers. - Identifier un élément par un segment :
/users/{id}. - Préférer des chemins courts, prévisibles, et stables dans le temps.
Étapes pour concevoir un endpoint lisible
Choisir la ressource cible : ex. utilisateurs.
Déterminer le niveau : collection (
/users) ou élément (/users/{id}).Déterminer l’opération : lecture, création, modification, suppression.
Choisir le verbe HTTP correspondant (voir section suivante).
Continuez dans notre application.- Écoutez le fichier audio avec l'écran éteint.
- Obtenez un certificat à la fin du programme.
- Plus de 5000 cours à découvrir !
Téléchargez l'application
Placer le filtrage/tri/pagination dans la query string (ex.
?status=active&sort=-createdAt).
Choix des verbes HTTP et correspondance avec CRUD
Dans une API REST, le verbe HTTP exprime l’intention. Les chemins restent orientés ressources.
| Opération | Verbe | Cible | Exemple |
|---|---|---|---|
| Lire une collection | GET | Collection | GET /users |
| Lire un élément | GET | Élément | GET /users/42 |
| Créer | POST | Collection | POST /users |
| Remplacer entièrement | PUT | Élément | PUT /users/42 |
| Modifier partiellement | PATCH | Élément | PATCH /users/42 |
| Supprimer | DELETE | Élément | DELETE /users/42 |
PUT vs PATCH (règle simple)
- PUT : le client envoie une représentation complète (ou considérée complète par contrat). Les champs omis peuvent être réinitialisés selon votre convention.
- PATCH : le client envoie uniquement les champs à modifier. Les champs omis ne changent pas.
Règles de cohérence : pluriels, hiérarchie, éviter les verbes
Pluriels et conventions de nommage
- Choisir une convention et s’y tenir : le plus courant est le pluriel pour les collections (
/users,/orders). - Utiliser des minuscules et des tirets si nécessaire :
/payment-methods. - Éviter les variations : ne pas mélanger
/useret/usersselon les endpoints.
Hiérarchie et sous-ressources
Une sous-ressource est pertinente quand elle dépend naturellement d’une ressource parente.
- Exemples :
/users/{userId}/orders(commandes d’un utilisateur),/orders/{orderId}/items(lignes d’une commande). - Éviter les hiérarchies trop profondes : au-delà de 2–3 niveaux, la lisibilité et l’évolutivité se dégradent.
Quand une ressource peut être adressée directement, proposer aussi un accès direct :
GET /orders/{orderId}en plus deGET /users/{userId}/orders(selon le besoin).
Éviter les verbes dans les chemins
Les verbes appartiennent au protocole HTTP. Préférer :
POST /usersplutôt quePOST /users/create.DELETE /users/42plutôt quePOST /users/42/delete.
Cas particulier : opérations qui ne sont pas un CRUD simple. Deux approches courantes :
- Sous-ressource : ex. réinitialisation de mot de passe via une ressource
/password-resets:POST /password-resets. - Action comme ressource : ex.
POST /sessionspour créer une session (authentification) plutôt quePOST /login.
Paramètres de requête : filtrage, tri, pagination
La query string sert à affiner une collection sans changer l’identité de la ressource.
Filtrage
Exemple : récupérer les utilisateurs actifs d’un rôle donné.
GET /users?status=active&role=admin- Utiliser des noms de paramètres explicites.
- Éviter de surcharger un paramètre unique (ex.
q=...) si plusieurs filtres structurés sont nécessaires.
Tri
Convention fréquente : sort avec un champ, et un préfixe - pour décroissant.
GET /users?sort=lastName,-createdAtPagination
Deux styles courants :
- Offset/limit : simple, mais moins stable si la collection change.
- Cursor : plus robuste pour de gros volumes.
Exemple offset/limit :
GET /users?limit=20&offset=40Exemple cursor :
GET /users?limit=20&cursor=eyJjcmVhdGVkQXQiOiIyMDI2LTAxLTE3VDA5OjAwOjAwWiIsImlkIjo0Mn0=Bonnes pratiques requêtes/réponses : en-têtes, formats, encodage
Content negotiation : Accept et Content-Type
- Requêtes avec corps (POST/PUT/PATCH) : envoyer
Content-Type: application/json; charset=utf-8. - Réponses : renvoyer
Content-Type: application/json; charset=utf-8. - Le client exprime le format attendu via
Accept: application/json.
Encodage UTF-8
Standardiser l’UTF-8 évite les erreurs sur les accents, noms, adresses, etc. Indiquer le charset dans le Content-Type et s’assurer que la sérialisation JSON est en UTF-8.
En-têtes utiles
Authorization: Bearer <token>: authentification.Idempotency-Key: sécuriser certainsPOST(ex. création de paiement) contre les doublons.ETagetIf-Match: contrôle de concurrence optimiste (éviter d’écraser une mise à jour).Location: indiquer l’URL de la ressource créée après unPOST.
Exemples détaillés de contrats d’API (requête, réponse, JSON attendu)
1) Lire une collection avec filtrage, tri et pagination
Requête
GET /users?status=active&sort=lastName&limit=2&offset=0 HTTP/1.1
Accept: application/jsonRéponse
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8{
"data": [
{
"id": "42",
"email": "alex.dupont@example.com",
"firstName": "Alex",
"lastName": "Dupont",
"status": "active",
"createdAt": "2026-01-10T14:12:03Z"
},
{
"id": "77",
"email": "samira.benali@example.com",
"firstName": "Samira",
"lastName": "Benali",
"status": "active",
"createdAt": "2026-01-12T09:01:55Z"
}
],
"meta": {
"limit": 2,
"offset": 0,
"total": 128
}
}2) Lire un élément
Requête
GET /users/42 HTTP/1.1
Accept: application/jsonRéponse
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8{
"data": {
"id": "42",
"email": "alex.dupont@example.com",
"firstName": "Alex",
"lastName": "Dupont",
"status": "active",
"createdAt": "2026-01-10T14:12:03Z"
}
}3) Créer une ressource (POST sur collection)
Requête
POST /users HTTP/1.1
Accept: application/json
Content-Type: application/json; charset=utf-8
Idempotency-Key: 9b2d9a2a-7c2f-4f2a-9c9a-2d0d2a9b1c11
{
"email": "lea.martin@example.com",
"firstName": "Léa",
"lastName": "Martin"
}Réponse (création réussie)
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
Location: /users/105{
"data": {
"id": "105",
"email": "lea.martin@example.com",
"firstName": "Léa",
"lastName": "Martin",
"status": "active",
"createdAt": "2026-01-17T10:22:11Z"
}
}Erreurs typiques
400 Bad Request: JSON invalide, champ manquant, format incorrect.409 Conflict: email déjà utilisé (conflit d’unicité).415 Unsupported Media Type:Content-Typeabsent ou non supporté.
4) Remplacer entièrement (PUT sur élément)
Requête (remplacement complet selon contrat)
PUT /users/42 HTTP/1.1
Accept: application/json
Content-Type: application/json; charset=utf-8
If-Match: "user-42-v3"
{
"email": "alex.dupont@example.com",
"firstName": "Alexandre",
"lastName": "Dupont",
"status": "active"
}Réponse
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
ETag: "user-42-v4"{
"data": {
"id": "42",
"email": "alex.dupont@example.com",
"firstName": "Alexandre",
"lastName": "Dupont",
"status": "active",
"updatedAt": "2026-01-17T10:30:00Z"
}
}Concurrence : si l’ETag ne correspond plus (ressource modifiée entre-temps), renvoyer :
HTTP/1.1 412 Precondition Failed
Content-Type: application/json; charset=utf-8
{
"error": {
"code": "PRECONDITION_FAILED",
"message": "Resource has been modified. Fetch the latest version and retry."
}
}5) Modifier partiellement (PATCH sur élément)
Requête
PATCH /users/42 HTTP/1.1
Accept: application/json
Content-Type: application/json; charset=utf-8
{
"status": "suspended"
}Réponse
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8{
"data": {
"id": "42",
"status": "suspended",
"updatedAt": "2026-01-17T10:35:10Z"
}
}6) Supprimer (DELETE sur élément)
Requête
DELETE /users/42 HTTP/1.1
Accept: application/jsonRéponse (deux options courantes)
- 204 No Content (souvent préféré) : pas de corps.
- 200 OK : si vous renvoyez un corps (ex. confirmation).
HTTP/1.1 204 No ContentExemples de sous-ressources : hiérarchie cohérente
Commandes d’un utilisateur
Lire les commandes d’un utilisateur
GET /users/42/orders?sort=-createdAt&limit=10 HTTP/1.1
Accept: application/jsonRéponse
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8{
"data": [
{
"id": "9001",
"status": "paid",
"total": 49.9,
"currency": "EUR",
"createdAt": "2026-01-15T08:10:00Z"
}
],
"meta": {
"limit": 10,
"total": 1
}
}Créer une sous-ressource
Créer une commande pour un utilisateur peut se faire via la sous-ressource :
POST /users/42/orders HTTP/1.1
Accept: application/json
Content-Type: application/json; charset=utf-8
{
"items": [
{ "productId": "p-1", "quantity": 2 }
]
}Réponse
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
Location: /orders/9002{
"data": {
"id": "9002",
"status": "pending",
"createdAt": "2026-01-17T10:40:00Z"
}
}Checklist de cohérence à appliquer à toute nouvelle route
- Le chemin décrit-il une ressource (nom) plutôt qu’une action (verbe) ?
- Collection au pluriel et élément via
/{id}? - Le verbe HTTP correspond-il à l’intention (GET/POST/PUT/PATCH/DELETE) ?
- Filtrage/tri/pagination uniquement en query string ?
AcceptetContent-Typesont-ils corrects, aveccharset=utf-8?- Les réponses utilisent-elles des codes HTTP cohérents (200/201/204/400/409/412/415) ?
- La structure JSON est-elle stable (ex. enveloppe
data,meta,error) ?