Aller au contenu
~/ blog / / laravel-api-approche-design-first

API Design-First avec Laravel : Une Implémentation Pratique

Appliquer la méthodologie design-first avec Laravel. Un complément au guide générique.

· 4 min read ·
Laravel API Design-First OpenAPI Bonnes Pratiques

J’ai longtemps codé mes APIs Laravel en mode “on verra bien”. Route, contrôleur, quelques tests, on ship. Ça marche — jusqu’au jour où le frontend attend un champ created_at et le backend renvoie date_creation. Trois heures de debug pour un problème de communication.

Le Design-First règle ça. On écrit le contrat avant le code. Tout le monde sait ce qui entre, ce qui sort, et dans quel format.


Le principe

Vous décrivez votre API dans une spec OpenAPI avant de toucher à Laravel. C’est le plan de la maison avant de couler la dalle.

Concrètement, ça donne trois choses :

  • Le frontend démarre sans attendre le backend (mock serveur sur la spec)
  • Les bugs d’intégration disparaissent quasi complètement
  • La doc est déjà faite quand le code est prêt

Écrire le contrat API

On commence par un fichier OpenAPI. Chaque endpoint y est décrit : méthode HTTP, paramètres, formats de requête et réponse, codes d’erreur.

openapi: 3.0.3
info:
  title: "API Utilisateurs"
  version: "1.0.0"
paths:
  /api/users/{id}:
    get:
      summary: Récupérer un utilisateur par ID
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: Utilisateur trouvé
        "404":
          description: Utilisateur introuvable

Valider la spec en équipe

Ce fichier, je le fais relire par le frontend, la QA, le product. Pas besoin d’une réunion formelle — une PR avec la spec suffit. Chacun commente. Une fois mergé, c’est la source de vérité. On ne négocie plus les formats en plein sprint.


Scaffolder les routes et contrôleurs

La spec est validée. On passe à Laravel.

php artisan make:controller Api/UserController

Dans routes/api.php :

use App\Http\Controllers\Api\UserController;

Route::get('/users/{id}', [UserController::class, 'show']);

Premier réflexe : un stub qui renvoie des données en dur. Le frontend peut brancher ses appels immédiatement.

public function show(int $id)
{
    return response()->json([
        'id' => $id,
        'name' => 'Utilisateur Démo',
    ]);
}

Brancher la vraie logique

Une fois le stub en place et le frontend qui tourne, on remplace par le vrai code :

use App\Models\User;

public function show(int $id)
{
    $user = User::find($id);

    if (!$user) {
        return response()->json(['error' => 'Utilisateur introuvable'], 404);
    }

    return response()->json($user, 200);
}

Le contrat ne change pas. Seule l’implémentation évolue. C’est tout l’intérêt.


Valider les entrées

La validation Laravel, c’est du solide. Je m’en sers pour garantir que chaque requête respecte la spec :

use Illuminate\Support\Facades\Validator;

public function store(Request $request)
{
    $validator = Validator::make($request->all(), [
        'name' => 'required|string|max:255',
        'email' => 'required|email|unique:users',
    ]);

    if ($validator->fails()) {
        return response()->json(['errors' => $validator->errors()], 400);
    }

    $user = User::create($request->validated());

    return response()->json($user, 201);
}

Des erreurs claires en 400, des données propres en base. Les devs frontend savent exactement quoi afficher quand ça échoue.


Tester contre la spec

Les tests vérifient que l’implémentation colle au contrat. Pas de surprise en prod.

// tests/Feature/UserApiTest.php
public function test_retrieve_existing_user_successfully()
{
    $user = User::factory()->create();

    $response = $this->getJson("/api/users/{$user->id}");

    $response->assertStatus(200)
             ->assertJson([
                 'id' => $user->id,
                 'email' => $user->email,
             ]);
}

public function test_retrieve_nonexistent_user_returns_404()
{
    $response = $this->getJson("/api/users/9999");

    $response->assertStatus(404)
             ->assertJson([
                 'error' => 'Utilisateur introuvable',
             ]);
}

Je lance ces tests en CI. Si un dev change un format de réponse sans mettre à jour la spec, le pipeline casse. C’est voulu.


Générer la doc

Dernier maillon : la documentation auto-générée depuis la spec. L5-Swagger fait le travail :

composer require darkaonline/l5-swagger
php artisan l5-swagger:generate

La doc reste synchronisée avec le code. Plus de wiki obsolète que personne ne met à jour.


Ce que ça change au quotidien

Sur mes derniers projets Laravel, le Design-First a tué une catégorie entière de bugs — ceux liés aux malentendus entre frontend et backend. Le temps investi dans la spec se récupère dès la première intégration. Et quand un nouveau dev rejoint l’équipe, la spec OpenAPI lui donne le contexte en cinq minutes au lieu d’une demi-journée de fouille dans le code.