Concevoir une REST API avec Claude Code: endpoints, erreurs, pagination, idempotence
Concevez une REST API avec Claude Code avant le code: OpenAPI, erreurs, pagination, idempotence et checklist.
Une REST API n’est pas terminée parce que quelques routes existent. Avant l’implémentation, il faut décider ce qui est une ressource, quelle méthode HTTP chaque opération utilise, ce que l’API renvoie en cas d’échec, comment les listes sont paginées et comment une même requête répétée est traitée. Si ces choix restent flous, Claude Code peut écrire vite, mais le frontend, l’application mobile et les intégrations partenaires comprendront chacun un contrat différent.
Cet article explique comment utiliser Claude Code pour verrouiller la conception d’une REST API avant de coder. REST signifie Representational State Transfer, mais dans le quotidien produit on peut le lire comme un ensemble de règles HTTP pour manipuler des ressources telles que commandes, utilisateurs ou factures. La ressource est l’objet, la méthode est l’action, le code de statut est le signal de résultat.
Pour les références officielles, utilisez HTTP request methods et HTTP response status codes de MDN, ainsi que l’OpenAPI Specification. Au 3 juin 2026, la page latest d’OpenAPI renvoie vers 3.2.0; l’exemple reste en openapi: 3.1.0 parce que de nombreux linters et générateurs le prennent encore mieux en charge.
Pour la suite côté implémentation, lisez aussi développer des APIs avec Claude Code, tester une API avec Claude Code et la revue de code avec Claude Code. Les modèles et checklists prêts à réutiliser sont dans /products/, et l’accompagnement d’équipe pour les revues de design API dans /training/.
Aligner Le Vocabulaire
Les débutants se trompent souvent parce que les termes REST se ressemblent. Avant de demander une implémentation à Claude Code, l’équipe doit partager ces définitions.
| Terme | Explication simple | Exemple |
|---|---|---|
| Ressource | Le nom que l’API manipule | orders, customers |
| Endpoint | Une URL plus une méthode HTTP | GET /v1/orders |
| Méthode | Le verbe HTTP qui indique l’action | GET, POST, PATCH |
| Code de statut | Un nombre qui signale succès, mauvaise entrée, authentification absente, etc. | 200, 201, 400, 404 |
| Validation | Vérification que la requête a la bonne forme | Format d’email, quantité minimale |
| Idempotence | Répéter la même opération ne change pas l’état final | Une seule commande pour la même clé |
Claude Code lit bien le contexte, mais il ne garantit pas de deviner votre intention de design. “Crée l’API de commande” peut produire /createOrder, /orders/create ou POST /orders. Donner le vocabulaire et les règles avant le code améliore fortement le résultat.
Décider Avec Trois Cas D’Usage
La même API de commandes n’a pas les mêmes priorités selon son usage.
Premier cas: l’ingestion B2B SaaS. Si un partenaire rejoue le même CSV de nuit, POST /v1/orders a besoin d’un Idempotency-Key pour éviter les doublons. Les rejouements arrivent souvent pendant la résolution d’incident; l’idempotence est difficile à ajouter proprement après coup.
Deuxième cas: la liste d’administration. Une personne filtre avec status=paid, passe à la page suivante, puis une autre crée une commande. page=2 peut alors se décaler. Utilisez une pagination par curseur avec limit et after, plus un tri stable comme createdAt desc, id desc.
Troisième cas: l’API mobile. Si d’anciennes versions de l’application restent installées plusieurs mois, les noms de champs en réponse ne peuvent pas changer brutalement. Les nouveaux champs obligatoires ou un nouveau format d’erreur doivent aller dans /v2, pendant que le contrat /v1 reste documenté dans OpenAPI. Un petit champ optionnel ne nécessite généralement pas de nouvelle version.
Quatrième cas fréquent: les APIs internes entre services. Les consommateurs sont plus faciles à mettre à jour ensemble, mais la supervision est souvent plus faible. Même en interne, des statuts et formats d’erreur incohérents forcent chaque appelant à écrire une logique spéciale.
Visualiser Le Flux De Design
Figez le design dans cet ordre avant de confier la tâche à Claude Code. Cela évite que l’implémentation dérive du contrat pendant les modifications de fichiers.
flowchart TD
A["Use cases"] --> B["Resources and endpoints"]
B --> C["OpenAPI contract"]
C --> D["Errors, pagination, idempotency"]
D --> E["Claude Code implementation"]
E --> F["Tests and review"]
Figer La Table Des Endpoints Avant De Coder
Le premier livrable n’est pas le code. C’est une table d’endpoints. Donnez-la à Claude Code pour que routes, OpenAPI, tests et documentation partent du même contrat.
| Objectif | Méthode et chemin | Succès | Règle importante |
|---|---|---|---|
| Lister les commandes | GET /v1/orders?status=paid&limit=20&after=ord_123 | 200 OK | Pagination par curseur avec tri stable |
| Créer une commande | POST /v1/orders | 201 Created | Accepter Idempotency-Key et renvoyer Location |
| Lire une commande | GET /v1/orders/{orderId} | 200 OK | Renvoyer 404 si absente |
| Mettre à jour | PATCH /v1/orders/{orderId} | 200 OK | Mise à jour partielle; patch vide = 400 |
| Annuler | POST /v1/orders/{orderId}/cancel | 200 OK | Traiter les transitions d’état de façon cohérente |
La règle générale est “des noms dans l’URL, des verbes dans les méthodes”. Certaines actions métier, comme annuler une commande, sont plus lisibles comme sous-action explicite que comme DELETE forcé. L’essentiel est d’écrire l’exception et de garder le même motif pour les opérations similaires.
Faire D’OpenAPI La Source Du Design
OpenAPI décrit chemins, paramètres, requêtes et réponses dans un format lisible par les outils. En disant à Claude Code que ce contrat OpenAPI est la source de vérité, vous limitez la liberté d’implémentation au bon endroit.
openapi: 3.1.0
info:
title: Acme Orders API
version: 1.0.0
servers:
- url: https://api.example.com
paths:
/v1/orders:
get:
operationId: listOrders
summary: List orders
parameters:
- $ref: "#/components/parameters/StatusParam"
- $ref: "#/components/parameters/LimitParam"
- $ref: "#/components/parameters/AfterParam"
responses:
"200":
description: Orders are returned in a stable cursor order.
content:
application/json:
schema:
$ref: "#/components/schemas/OrderListResponse"
"400":
$ref: "#/components/responses/BadRequest"
post:
operationId: createOrder
summary: Create an order
parameters:
- $ref: "#/components/parameters/IdempotencyKey"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateOrderRequest"
responses:
"201":
description: Order created.
headers:
Location:
schema:
type: string
description: URL of the created order.
content:
application/json:
schema:
$ref: "#/components/schemas/OrderResponse"
"400":
$ref: "#/components/responses/BadRequest"
"409":
$ref: "#/components/responses/Conflict"
/v1/orders/{orderId}:
get:
operationId: getOrder
summary: Get one order
parameters:
- $ref: "#/components/parameters/OrderId"
responses:
"200":
description: Order found.
content:
application/json:
schema:
$ref: "#/components/schemas/OrderResponse"
"404":
$ref: "#/components/responses/NotFound"
patch:
operationId: updateOrder
summary: Update mutable order fields
parameters:
- $ref: "#/components/parameters/OrderId"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/UpdateOrderRequest"
responses:
"200":
description: Order updated.
content:
application/json:
schema:
$ref: "#/components/schemas/OrderResponse"
"400":
$ref: "#/components/responses/BadRequest"
"404":
$ref: "#/components/responses/NotFound"
components:
parameters:
OrderId:
name: orderId
in: path
required: true
schema:
type: string
StatusParam:
name: status
in: query
schema:
type: string
enum: [draft, paid, canceled]
LimitParam:
name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 20
AfterParam:
name: after
in: query
schema:
type: string
description: Cursor returned by the previous response.
IdempotencyKey:
name: Idempotency-Key
in: header
required: false
schema:
type: string
minLength: 16
maxLength: 128
schemas:
CreateOrderRequest:
type: object
required: [customerId, items]
properties:
customerId:
type: string
items:
type: array
minItems: 1
items:
type: object
required: [sku, quantity]
properties:
sku:
type: string
quantity:
type: integer
minimum: 1
UpdateOrderRequest:
type: object
minProperties: 1
properties:
status:
type: string
enum: [draft, paid, canceled]
OrderResponse:
type: object
required: [data]
properties:
data:
$ref: "#/components/schemas/Order"
OrderListResponse:
type: object
required: [data, pageInfo]
properties:
data:
type: array
items:
$ref: "#/components/schemas/Order"
pageInfo:
type: object
required: [nextCursor, hasMore]
properties:
nextCursor:
type: [string, "null"]
hasMore:
type: boolean
Order:
type: object
required: [id, status, totalCents, createdAt]
properties:
id:
type: string
status:
type: string
totalCents:
type: integer
createdAt:
type: string
format: date-time
ErrorResponse:
type: object
required: [error]
properties:
error:
type: object
required: [code, message, requestId]
properties:
code:
type: string
message:
type: string
requestId:
type: string
details:
type: array
items:
type: object
responses:
BadRequest:
description: Request validation failed.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
Conflict:
description: Idempotency key conflicts with a different request.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
NotFound:
description: Resource was not found.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
Enregistrez ce contenu dans openapi.yaml et demandez à Claude Code de ne pas proposer d’implémentation qui s’en écarte. Ensuite, npx @redocly/cli lint openapi.yaml peut vérifier syntaxe et références. Si vous générez des types ou clients TypeScript, relisez d’abord le sens humain de la table d’endpoints.
Uniformiser Erreurs Et Codes De Statut
Le code de statut est le signal HTTP externe. Le JSON d’erreur est le détail lu par les humains et les programmes. Renvoyer 200 OK avec { "success": false } fait croire au monitoring, aux SDKs et aux retries que tout va bien. Fixez la table: mauvaise entrée = 400, absence d’authentification = 401, droit insuffisant = 403, ressource absente = 404, doublon ou conflit d’idempotence = 409.
{
"error": {
"code": "VALIDATION_FAILED",
"message": "Request body has invalid fields.",
"requestId": "req_01J0RESTAPI001",
"details": [
{
"field": "items[0].quantity",
"reason": "must be greater than or equal to 1"
}
]
}
}
{
"error": {
"code": "IDEMPOTENCY_CONFLICT",
"message": "The same Idempotency-Key was used with a different request body.",
"requestId": "req_01J0RESTAPI002"
}
}
{
"error": {
"code": "ORDER_NOT_FOUND",
"message": "Order was not found.",
"requestId": "req_01J0RESTAPI003"
}
}
Pour débuter: code est le nom court utilisé par le programme, message est l’explication lisible dans les logs ou l’interface, et requestId sert à retrouver les logs serveur. details est surtout utile pour les erreurs de validation champ par champ.
Ne Pas Laisser Pagination, Idempotence Et Version Floues
Les APIs de liste ont besoin de pagination dès le départ. Tout renvoyer fonctionne avec peu de données, mais changer la réponse plus tard devient une rupture de contrat. Avec une pagination par curseur, le client renvoie le nextCursor précédent dans after.
{
"data": [
{
"id": "ord_100",
"status": "paid",
"totalCents": 4800,
"createdAt": "2026-06-03T09:00:00Z"
}
],
"pageInfo": {
"nextCursor": "ord_099",
"hasMore": true
}
}
L’idempotence évite que des requêtes répétées produisent des effets doublés. POST /orders est particulièrement concerné, car les clients réessaient après une erreur réseau. Stockez Idempotency-Key et un hash du body pendant une durée limitée. Même clé et même body: renvoyer le résultat précédent. Même clé et body différent: renvoyer 409.
Le versioning donne du temps de migration. Supprimer un champ requis, changer un type, changer le tri par défaut ou ajouter un champ requis dans la requête est une rupture et doit aller vers /v2. Ajouter un champ optionnel peut généralement rester dans /v1.
Demander Une Revue À Claude Code
Avant d’implémenter, utilisez un prompt de revue précis.
Review this OpenAPI design.
Check:
- Resource names are nouns
- HTTP methods and status codes match MDN semantics
- 400/401/403/404/409/500 errors share the same JSON shape
- List pagination is unambiguous
- POST creation endpoints that need idempotency keys are identified
- No response change breaks the v1 contract
- Return a table of issues to fix before TypeScript implementation and tests
Claude Code agit ainsi comme reviewer de design avant de modifier les fichiers. Lancez l’implémentation seulement après avoir sauvegardé l’OpenAPI relu.
Erreurs Fréquentes
Première erreur: les endpoints en style action. Si POST /createOrder, POST /updateOrderStatus et GET /deleteOrder coexistent, l’URL n’explique plus clairement la ressource et l’opération. Préférez POST /v1/orders, PATCH /v1/orders/{orderId} et DELETE /v1/orders/{orderId}, puis documentez les exceptions.
Deuxième erreur: les codes de statut incohérents. Si une mauvaise entrée renvoie 500, une ressource absente renvoie 200, et une création réussie alterne entre 200 et 201, les clients ne peuvent pas décider correctement. Standardisez les codes de succès comme les erreurs.
Troisième erreur: la pagination ambiguë. Si page=2 ne précise pas si l’ordre suit la création ou la mise à jour, les nouvelles données créent doublons ou trous. Documentez le tri, le limit maximal et la détection de page suivante.
Quatrième erreur: cacher la validation uniquement dans l’implémentation. Si quantity >= 1 existe dans le code mais pas dans OpenAPI, le frontend et les SDKs générés ne peuvent pas réutiliser la règle. Les contraintes doivent vivre dans la spécification et le code.
Checklist De Revue
- Les ressources sont des noms pluriels cohérents
- Les méthodes respectent le sens de
GET,POST,PATCHetDELETE - Les succès distinguent
200,201et204 400,401,403,404,409et500partagent le même JSON- Les listes contiennent
limit,after,nextCursorethasMore - Les valeurs par défaut et maximales de
limitsont écrites - Les créations ne dupliquent pas les données lors des retries
- Les critères pour passer les ruptures en
/v2sont explicites - Les règles de validation apparaissent dans OpenAPI
- La table d’endpoints et OpenAPI concordent avant de donner la tâche à Claude Code
Concevoir une REST API ne consiste pas à produire de jolies URLs. Il s’agit de transformer des promesses faites aux consommateurs en texte lisible et en spécification exploitable par les outils. Claude Code accélère l’implémentation, mais les promesses à tenir restent une décision humaine.
Lors du test sur l’API de commandes de Masa, préparer OpenAPI, la table d’endpoints et les JSON d’erreur avant de demander l’implémentation à Claude Code a nettement réduit les retours de revue. L’ajout de Idempotency-Key et de la pagination par curseur dès la conception a aussi évité des correctifs tardifs contre les commandes dupliquées et les éléments manquants dans les listes.
PDF gratuit: cheatsheet Claude Code
Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.
Nous protégeons vos données et n'envoyons pas de spam.
À propos de l'auteur
Masa
Ingénieur spécialisé dans les workflows pratiques avec Claude Code.
Articles liés
Échelle de sécurité des permissions Claude Code
Passer du read-only aux éditions limitées, preuves et checks de déploiement sans perdre le contrôle.
Claude Code Small PR Proof Pack : rendre les petits changements reviewables
Un pack de preuve pour PR Claude Code : diff, vérifications, URL publique, CTA et rollback.
Gate de review avant commit avec Claude Code
Review avant commit avec Claude Code : diff, build, URL publique, liens Gumroad, CTA consultation, tests manquants et fichiers hors scope.