DEV Community

Cover image for Qu'est-ce que le développement API Spec-First ?
Antoine Laurent
Antoine Laurent

Posted on • Originally published at apidog.com

Qu'est-ce que le développement API Spec-First ?

La plupart des bugs d'API ne viennent pas d'une erreur de code, mais d'un désaccord de contrat. Le frontend attend userId, le backend renvoie user_id, et l'écart n'est détecté qu'en QA. Le développement d'API spec-first évite ce problème en faisant de la spécification OpenAPI le premier artefact du projet, avant le code serveur.

Essayez Apidog aujourd’hui

Dans ce guide, vous allez rédiger une petite spécification OpenAPI, puis l'utiliser pour générer des mocks, préparer des tests de contrat et produire une documentation API avant même que le backend soit implémenté. Cette approche est aussi appelée design-first, contract-first ou développement piloté par les spécifications. Le principe reste le même : définir l'interface, la valider, puis construire.

Qu'est-ce que le développement d'API spec-first ?

Le développement d'API spec-first consiste à écrire un contrat lisible par machine, généralement un fichier OpenAPI, avant d'implémenter les endpoints. Ce contrat décrit :

  • les chemins ;
  • les méthodes HTTP ;
  • les paramètres ;
  • les corps de requête ;
  • les schémas de réponse ;
  • les codes de statut ;
  • les erreurs attendues.

La spécification n'est donc pas une documentation écrite après coup. Elle devient la source de vérité du projet.

Concrètement :

  • le frontend consomme un serveur mock généré depuis la spec ;
  • l'équipe QA prépare des tests à partir des schémas ;
  • le backend implémente les endpoints pour respecter le contrat ;
  • la documentation est générée depuis le même fichier.

Dans une approche code-first, vous écrivez les handlers, puis vous documentez l'API ensuite. La documentation finit souvent par diverger du code. Dans une approche spec-first, vous inversez l'ordre : le contrat est écrit, relu et validé avant l'implémentation.

Cycle de vie spec-first vs code-first

Les deux approches peuvent produire les mêmes endpoints. La différence principale est le moment où les problèmes apparaissent.

Cycle de vie spec-first vs code-first

Avec une approche spec-first, les équipes peuvent travailler en parallèle dès que le contrat est validé. Le frontend n'attend pas le backend, la QA n'attend pas une version stable, et la documentation ne dépend pas d'une rédaction manuelle en fin de sprint.

Exemple pratique : créer une spec OpenAPI pour /users

Construisons une API simple avec deux opérations :

  • GET /users pour lister les utilisateurs ;
  • POST /users pour créer un utilisateur.

1. Déclarer les métadonnées OpenAPI

Commencez par l'en-tête du document :

openapi: 3.0.3
info:
  title: Users API
  version: 1.0.0
servers:
  - url: https://api.example.com/v1
Enter fullscreen mode Exit fullscreen mode

Cette partie indique :

  • la version OpenAPI utilisée ;
  • le nom de l'API ;
  • la version du contrat ;
  • l'URL de base du serveur.

2. Définir le schéma User

Placez les modèles réutilisables dans components/schemas.

components:
  schemas:
    User:
      type: object
      required:
        - id
        - email
        - createdAt
      properties:
        id:
          type: string
          format: uuid
        email:
          type: string
          format: email
        name:
          type: string
        createdAt:
          type: string
          format: date-time
Enter fullscreen mode Exit fullscreen mode

Ce schéma impose que chaque utilisateur possède :

  • un id au format UUID ;
  • un email valide ;
  • une date createdAt ;
  • un champ optionnel name.

L'avantage de components/schemas est la réutilisation. Vous définissez User une seule fois, puis vous le référencez dans plusieurs endpoints avec $ref.

3. Définir GET /users

Ajoutez ensuite l'opération de lecture.

paths:
  /users:
    get:
      summary: List users
      operationId: listUsers
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
            maximum: 100
      responses:
        "200":
          description: A list of users
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/User"
Enter fullscreen mode Exit fullscreen mode

Ce contrat indique que :

  • limit est un paramètre de query string ;
  • sa valeur par défaut est 20 ;
  • sa valeur maximale est 100 ;
  • la réponse 200 renvoie un tableau d'objets User.

Un client peut donc appeler :

GET /users?limit=10
Enter fullscreen mode Exit fullscreen mode

Et attendre une réponse de ce type :

[
  {
    "id": "3f8f3c5e-4d3e-4b22-9d3a-7c9e77f7d8f1",
    "email": "alice@example.com",
    "name": "Alice",
    "createdAt": "2026-06-01T10:00:00Z"
  }
]
Enter fullscreen mode Exit fullscreen mode

4. Définir POST /users

Ajoutez maintenant l'opération de création.

paths:
  /users:
    post:
      summary: Create a user
      operationId: createUser
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
              properties:
                email:
                  type: string
                  format: email
                name:
                  type: string
      responses:
        "201":
          description: User created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"
        "400":
          description: Invalid request body
Enter fullscreen mode Exit fullscreen mode

Le corps de requête attendu est minimal :

{
  "email": "alice@example.com",
  "name": "Alice"
}
Enter fullscreen mode Exit fullscreen mode

Le champ email est obligatoire. Si le client l'omet, l'API doit pouvoir répondre avec 400.

5. Assembler la spécification complète

Voici le fichier complet :

openapi: 3.0.3
info:
  title: Users API
  version: 1.0.0

servers:
  - url: https://api.example.com/v1

paths:
  /users:
    get:
      summary: List users
      operationId: listUsers
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
            maximum: 100
      responses:
        "200":
          description: A list of users
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/User"

    post:
      summary: Create a user
      operationId: createUser
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
              properties:
                email:
                  type: string
                  format: email
                name:
                  type: string
      responses:
        "201":
          description: User created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"
        "400":
          description: Invalid request body

components:
  schemas:
    User:
      type: object
      required:
        - id
        - email
        - createdAt
      properties:
        id:
          type: string
          format: uuid
        email:
          type: string
          format: email
        name:
          type: string
        createdAt:
          type: string
          format: date-time
Enter fullscreen mode Exit fullscreen mode

À ce stade, aucun code serveur n'existe encore. Pourtant, vous avez déjà défini :

  • les endpoints disponibles ;
  • les entrées acceptées ;
  • les réponses attendues ;
  • les champs obligatoires ;
  • les formats de données ;
  • les erreurs de validation.

C'est ce contrat qui va piloter la suite.

Générer des mocks, des tests et de la documentation

L'intérêt du spec-first est de produire plusieurs artefacts à partir d'un seul fichier.

1. Générer un serveur mock

Un serveur mock lit la spécification OpenAPI et retourne des réponses conformes aux schémas.

Pour GET /users, le mock peut renvoyer un tableau d'utilisateurs respectant User. Les formats comme uuid, email et date-time aident les outils à générer des données réalistes.

Résultat : le frontend peut commencer à intégrer l'API immédiatement.

Exemple d'appel côté client :

const response = await fetch("https://mock.example.com/users?limit=10");
const users = await response.json();

console.log(users);
Enter fullscreen mode Exit fullscreen mode

Le backend réel n'est pas encore disponible, mais l'interface est déjà testable.

2. Écrire des tests de contrat

La spécification sert aussi d'oracle de test. Les tests ne vérifient pas seulement que l'API répond. Ils vérifient qu'elle respecte le contrat.

Pour POST /users, les assertions principales sont :

  • une requête valide retourne 201 ;
  • la réponse respecte le schéma User ;
  • une requête sans email retourne 400.

Exemple de logique de test :

import assert from "node:assert";

const response = await fetch("https://api.example.com/v1/users", {
  method: "POST",
  headers: {
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    email: "alice@example.com",
    name: "Alice"
  })
});

assert.strictEqual(response.status, 201);

const body = await response.json();

assert.ok(body.id);
assert.ok(body.email);
assert.ok(body.createdAt);
Enter fullscreen mode Exit fullscreen mode

Dans un pipeline CI, ces tests permettent de détecter rapidement une divergence entre la spec et l'implémentation.

3. Générer la documentation API

La documentation de référence peut être générée directement depuis le fichier OpenAPI.

Cela évite de maintenir séparément :

  • le code ;
  • les tests ;
  • les mocks ;
  • la documentation.

Chaque endpoint, paramètre, schéma et code de statut vient de la même source.

C'est aussi ce qui rend l'approche compatible avec un flux de travail API natif Git : la spécification est un fichier texte, versionné, relu et validé dans les pull requests.

Un reviewer peut voir dans un diff qu'un champ obligatoire a été supprimé ou qu'un paramètre a été renommé avant que le changement atteigne la production.

Le faire dans Apidog

Apidog prend en charge ce workflow via le mode Spec-First. Au lieu de traiter OpenAPI comme une simple exportation, Apidog utilise la spécification comme base du projet.

Mode Spec-First dans Apidog

Le workflow est le suivant :

  1. créez ou importez votre fichier OpenAPI ;
  2. éditez directement le YAML ;
  3. laissez Apidog générer les mocks ;
  4. partagez la documentation générée ;
  5. exécutez les opérations comme cas de test ;
  6. synchronisez la spec avec Git.

Pour l'exemple /users, vous pouvez coller la spécification dans l'éditeur. Apidog analyse les opérations GET /users et POST /users, puis met en place un serveur mock correspondant. Le frontend peut alors commencer à appeler ces endpoints sans attendre le backend.

Lorsque l'implémentation réelle est prête, vous pouvez exécuter les cas issus de la spécification contre le serveur backend et vérifier que les réponses correspondent aux schémas déclarés.

La synchronisation Git bidirectionnelle permet de garder le contrat au même endroit logique : le dépôt. Si vous modifiez le YAML dans votre éditeur et poussez le changement, Apidog le récupère. Si vous modifiez la spec dans Apidog, le changement peut apparaître comme un commit à relire.

Pour une comparaison plus détaillée, consultez spec-first vs design-first dans Apidog.

Checklist spec-first avant implémentation

Avant de démarrer le développement backend, vérifiez les points suivants :

  • [ ] La spécification est valide selon le schéma OpenAPI.
  • [ ] Chaque endpoint possède au moins une réponse de succès.
  • [ ] Chaque endpoint déclare au moins une réponse d'erreur.
  • [ ] Les schémas partagés sont placés dans components/schemas.
  • [ ] Les schémas réutilisés sont référencés avec $ref.
  • [ ] Les champs obligatoires sont marqués avec required.
  • [ ] Les formats utiles sont définis : uuid, email, date-time, etc.
  • [ ] Les paramètres de query, path et header sont explicitement décrits.
  • [ ] La spec est versionnée dans Git.
  • [ ] Les changements de contrat passent par une pull request.
  • [ ] Un serveur mock est généré depuis la spec.
  • [ ] Le frontend peut consommer le mock.
  • [ ] Les tests de contrat comparent les réponses réelles à la spec.
  • [ ] La documentation publiée est générée depuis le même fichier.

Si cette checklist est validée, les équipes peuvent avancer en parallèle sur un contrat commun au lieu de travailler à partir d'hypothèses différentes.

FAQ

Le développement d'API spec-first est-il la même chose que le design-first ?

Oui, dans la plupart des cas. Les termes design-first, contract-first et spec-first décrivent le même principe : définir l'interface avant l'implémentation.

Le terme spec-first insiste sur l'artefact concret utilisé au départ : un fichier de spécification OpenAPI.

Dois-je écrire le YAML à la main ?

Non. Vous pouvez utiliser un éditeur visuel ou écrire le YAML directement. L'important est que le contrat soit défini et accepté avant le code.

Beaucoup d'équipes combinent les deux : conception visuelle pour aller vite, puis relecture du YAML dans Git pour valider les détails.

Comment éviter que la spécification et le code divergent ?

Faites de la spécification la source de vérité.

En pratique :

  • gardez le fichier OpenAPI dans Git ;
  • relisez chaque changement dans une pull request ;
  • générez les mocks et la documentation depuis ce fichier ;
  • exécutez des tests de contrat en CI ;
  • faites échouer le pipeline si une réponse ne respecte plus le schéma.

Avec ce workflow, une divergence devient visible dès le développement, pas en QA ou en production.

Le développement d'API spec-first est un changement simple : écrire le contrat avant le code. Mais ce changement réduit fortement les erreurs d'intégration. Commencez par une spécification OpenAPI minimale, générez vos mocks, validez vos tests de contrat, puis implémentez le backend pour satisfaire le contrat.

Si vous voulez tester le cycle complet, ouvrez le mode Spec-First dans Apidog et connectez-le à votre dépôt Git.

Top comments (0)