Tips & Tricks (Mis à jour: 03/06/2026)

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.

Concevoir une REST API avec Claude Code: endpoints, erreurs, pagination, idempotence

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.

TermeExplication simpleExemple
RessourceLe nom que l’API manipuleorders, customers
EndpointUne URL plus une méthode HTTPGET /v1/orders
MéthodeLe verbe HTTP qui indique l’actionGET, POST, PATCH
Code de statutUn nombre qui signale succès, mauvaise entrée, authentification absente, etc.200, 201, 400, 404
ValidationVérification que la requête a la bonne formeFormat d’email, quantité minimale
IdempotenceRépéter la même opération ne change pas l’état finalUne 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.

ObjectifMéthode et cheminSuccèsRègle importante
Lister les commandesGET /v1/orders?status=paid&limit=20&after=ord_123200 OKPagination par curseur avec tri stable
Créer une commandePOST /v1/orders201 CreatedAccepter Idempotency-Key et renvoyer Location
Lire une commandeGET /v1/orders/{orderId}200 OKRenvoyer 404 si absente
Mettre à jourPATCH /v1/orders/{orderId}200 OKMise à jour partielle; patch vide = 400
AnnulerPOST /v1/orders/{orderId}/cancel200 OKTraiter 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, PATCH et DELETE
  • Les succès distinguent 200, 201 et 204
  • 400, 401, 403, 404, 409 et 500 partagent le même JSON
  • Les listes contiennent limit, after, nextCursor et hasMore
  • Les valeurs par défaut et maximales de limit sont écrites
  • Les créations ne dupliquent pas les données lors des retries
  • Les critères pour passer les ruptures en /v2 sont 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.

#claude-code #rest-api #openapi #typescript #backend
Gratuit

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.

Masa

À propos de l'auteur

Masa

Ingénieur spécialisé dans les workflows pratiques avec Claude Code.