API REST back-end : concevoir des endpoints lisibles et cohérents

Capítulo 2

Temps de lecture estimé : 7 minutes

+ Exercice

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 : /users plutô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

  1. Choisir la ressource cible : ex. utilisateurs.

  2. Déterminer le niveau : collection (/users) ou élément (/users/{id}).

  3. Déterminer l’opération : lecture, création, modification, suppression.

  4. 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 !
    Ou poursuivez votre lecture ci-dessous...
    Download App

    Téléchargez l'application

  5. 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érationVerbeCibleExemple
Lire une collectionGETCollectionGET /users
Lire un élémentGETÉlémentGET /users/42
CréerPOSTCollectionPOST /users
Remplacer entièrementPUTÉlémentPUT /users/42
Modifier partiellementPATCHÉlémentPATCH /users/42
SupprimerDELETEÉlémentDELETE /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 /user et /users selon 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 de GET /users/{userId}/orders (selon le besoin).

Éviter les verbes dans les chemins

Les verbes appartiennent au protocole HTTP. Préférer :

  • POST /users plutôt que POST /users/create.
  • DELETE /users/42 plutôt que POST /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 /sessions pour créer une session (authentification) plutôt que POST /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,-createdAt

Pagination

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=40

Exemple 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 certains POST (ex. création de paiement) contre les doublons.
  • ETag et If-Match : contrôle de concurrence optimiste (éviter d’écraser une mise à jour).
  • Location : indiquer l’URL de la ressource créée après un POST.

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/json

Ré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/json

Ré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-Type absent 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/json

Ré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 Content

Exemples 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/json

Ré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 ?
  • Accept et Content-Type sont-ils corrects, avec charset=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) ?

Répondez maintenant à l’exercice sur le contenu :

Quel design d’endpoint respecte le principe REST « chemin = ressource, verbe HTTP = action » pour supprimer un utilisateur identifié ?

Tu as raison! Félicitations, passez maintenant à la page suivante

Vous avez raté! Essayer à nouveau.

En REST, le chemin doit décrire une ressource stable (ici /users/42) et l’action est portée par le verbe HTTP. Pour une suppression d’un élément identifié, on utilise DELETE sur l’URL de l’élément.

Chapitre suivant

API REST back-end : statuts HTTP et sémantique des réponses

Arrow Right Icon
Couverture de livre électronique gratuite Développement back-end : concevoir une API REST propre
25%

Développement back-end : concevoir une API REST propre

Nouveau cours

8 pages

Téléchargez l'application pour obtenir une certification gratuite et écouter des cours en arrière-plan, même avec l'écran éteint.