Version dans le chemin (URI)
Exemple : /api/v1/invoices, /api/v2/invoices. Avantages : lisible dans les logs, facile à
routager dans Symfony (prefix / attributs de route), compatible avec des règles de cache CDN très
explicites. Inconvénient : les URL « vivent » longtemps — il faut une politique de décommission claire.
# config/routes.yaml (extrait illustratif) api_v1: resource: ../src/Controller/Api/V1/ type: attribute prefix: /api/v1
api_v2: resource: ../src/Controller/Api/V2/ type: attribute prefix: /api/v2
Version par en-tête ou media type
Exemple : Accept: application/vnd.example.v2+json. Utile quand vous voulez une URL stable et que vos
clients savent configurer des en-têtes (peu fréquent côté navigateur pur, plus courant serveur-à-serveur). Symfony
peut discriminer via Request::getPreferredFormat() ou des RequestMatcher — mais la
documentation OpenAPI doit être irréprochable sinon les intégrations dérivent.
Ce que les proxies et caches voient
Si une ressource change de forme sans changer d’URL ni d’en-tête de variance, vous risquez des
réponses mises en cache incohérentes. Avec URI versionnée, la clé de cache est naturellement séparée.
Avec Accept, configurez Vary: Accept et vérifiez le comportement de votre CDN.
Contrôleur : garder le métier partagé
Anti-pattern fréquent : dupliquer 80 % de la logique entre V1 et V2. Mieux : services applicatifs stables, adapters minces par version pour la forme de la réponse :
// Pseudo-code : même service, représentation différente. final class InvoiceController { public function __construct(private InvoiceRepository $repo) {}#[Route('/invoices/{id}', methods: ['GET'])] public function showV1(string $id, InvoiceV1Normalizer $n): JsonResponse { return new JsonResponse($n->normalize($this->repo->get(Uuid::fromString($id)))); }
}
FAQ
Faut-il supporter V1 indéfiniment ?
Non : fixez une date ou une release de fin, communiquez-la dans la doc et renvoyez des en-têtes
Deprecation / Sunset lorsque pertinent pour les clients API.
GraphQL remplace-t-il le versionnement REST ?
Ce sont des compromis différents : GraphQL déplace la complexité (schéma, N+1, auth par champ). Ce n’est pas un raccourci magique pour éviter le contrat et la compatibilité.