Accueil / Blog / Métier / 2016 / Django Rest Framework : versioning pour les API REST (hors série)

Django Rest Framework : versioning pour les API REST (hors série)

Par Yannick Chabbert — publié 08/02/2016
À tort, le versioning est souvent une étape négligée au début d'un projet d'API REST : pas grave, on verra ça vite fait plus tard... Et puis c'est le drame. Ça prend bien plus de temps que prévu. On découvre après coup que ça n'est pas aussi simple et qu'il vaut mieux y être préparé. Qui veut la paix, prépare la guerre !
Django Rest Framework : versioning pour les API REST (hors série)

Avant de se focaliser sur l'intégration du versioning avec DRF, on va balayer ensemble quelques questions d'ordre générale dans cet article. Oui c'est vrai, pourquoi avoir un article générique dans la suite des articles sur DRF ? Tout simplement parce qu'il s'inscrit dans la continuité de la "saga" et qu'il a été écrit uniquement dans un contexte autour de DRF. Et puis pour être honnête... l'article original était bien trop long à lire. Sérieux, fallait prendre une RTT !

Doit-on versionner ?

Non. Cet article ne sert à rien. Bonne journée ! Bon ok...en fait, c'est une question bien plus complexe qu'il n'y paraît et sujette à de nombreux débats.

Même si le Papa du REST Roy Fielding le déconseille aux plus téméraires, oui il faut versionner ! Si on lit entre les lignes, Papa REST et les défenseurs des systèmes non-versionnés ne sont pas aussi radicaux. Ils nous mettent en garde. Un seul serveur d'API public ne devrait pas forcer tous ses clients à s'adapter à ses nouveaux caprices de petit prince. Au contraire, les API REST existent justement pour éviter ce genre de problème. En théorie, le serveur et les clients ne sont pas censés être intimement liés entre eux. De telle sorte que des changements côté serveur n'impliquent pas des changements côté client. Pour accentuer cette séparation, il existe en plus certains principes comme hypermedia (HATEOAS) : le client doit seulement connaître l'url d'une ressource et ses actions. Le client n'a plus besoin de connaître toutes les urls de la ressource ni toutes les actions possibles pour un état donné.

"C'est bien sympa tout ça, mais dans la vraie vie, comment je peux m'assurer que je n'introduirai jamais de changements inévitables pour mes clients" ? Boule de cristal ? Marabout ? Non en effet, cela semble utopique. Même avec les plus belles techniques. C'est la raison pour laquelle il faut prévoir un système de versioning. En revanche, il faut s'en servir le moins possible. Comprenez par là que vous devez éviter à tout prix des changements d'implémentation pour vos clients. En bon serveur corporate, vous assumez au maximum tous les changements de votre côté. Même si ça vous demande beaucoup plus de travail. Quand ça n'est plus possible (refonte de l'architecture, grand ménage, etc), alors passez sur une version supérieure et prévenez vos clients. On a tous été client de diverses API, il est donc facile de comprendre à quel point il est pénible de mettre à jour régulièrement son code source juste parce que les services changent d'avis tous les quatre matins...

Comment versionner ?

Voici une liste non exhaustive des méthodes les plus fréquemment employées :

  • entête HTTP Accept :

    Accept: application/json; version=1.0
    
  • chemin d'url :

    https://foo.com/v1/books/
    
  • paramètre GET :

    https://foo.com/books?version=v1
    
  • nom de domaine:

    https://v1.foo.com/books/
    
  • entête HTTP spécifique :

    api-version: 2
    

Aujourd'hui, la technique de l'entête HTTP Accept et du chemin d'url sont les plus utilisées.

Quand faut-il versionner ?

Le plus tôt possible. Idéalement, vous demandez à vos clients de fournir une version dès le début du projet. Même si il n'y a qu'une version. Pourquoi ? Vos clients seront forcés d'implémenter le système de versioning. Comme ça plus tard, vous pourrez l'utiliser quand vous en aurez vraiment besoin. En effet, introduire un système de versioning plus tard pour vous et vos clients est pénible alors si vous pouvez éviter ça à tout le monde...

Quelle méthode choisir ?

Beaucoup de débats... Il y a de nombreux défenseurs de la technique de l'url parce que "c'est trop cool dans un navigateur Web, je peux me balader dans l'API et la tester". Humhum... En fait, j'ai du mal à comprendre comment on peut tester une REST API avec un navigateur Web... J'ai sûrement pas pigé le truc. On tape l'url dans la barre d'adresse et on regarde le résultat dans la fenêtre, c'est ça ? Pour des requêtes HTTP qui ne sont pas des méthodes GET, c'est pas super pratique non ?

Évidemment, je ne parle pas des plugins de navigateur Web comme l'excellent Postman pour Google Chrome. Pour le coup, un outil comme celui-ci nous permet d'utiliser plein d'autres techniques de versioning sans perdre en confort, comme les entêtes HTTP par exemple. Dans le même esprit, si vous préférez les clients lourds, je suis certain qu'il en existe toute une pléthore. Je ne peux que vous conseiller l'usage de ce genre d'outils au profit des documentations HTML comme Swagger car ils permettent aux clients de comprendre vraiment ce qu'ils font. Par exemple, un input type file peut vite faire oublier que l'on doit fournir un entête HTTP Content-Type: multipart/form-data avec un body dont un des part est le contenu du fichier... Non ? Trop fort.

Non, on ne choisit pas un système de versioning juste parce que c'est trop cool pour les développeurs qui veulent utiliser un navigateur Web. On choisit un système de versioning pour l'implémentation finale des clients en production. Et la plupart du temps, un client HTTP s'en fiche pas mal d'avoir la version dans l'url ou dans un header HTTP... Sinon, faut changer de librairie HTTP. Tout de suite.

Malheureusement, pas de solution miracle. Je ne peux pas vous conseiller une technique plutôt qu'une autre car cela dépend beaucoup du contexte. Si votre API a pour vocation d'être en lecture seule et d'avoir très peu de points d'entrées, alors l'url est peut être un bon choix. Si vous souhaitez faire du cache anonyme avec des reverse proxy cache, peut-être que là aussi la méthode par url serait un bon choix. En fait, si vos clients utilisent uniquement l'url, alors vous avez la réponse... C'est le cas par exemple des widgets HTML type oEmbed. En revanche, si vos clients utilisent forcément une librairie HTTP (opérations d'écritures, entête HTTP, etc), alors j'aurais plutôt tendance à vous conseiller la technique de l'entête HTTP Accept pour avoir des urls libres. En effet, certains prétendent que l'url doit seulement exprimer une action sur une ressource, la version n'en faisant pas partie. Pourquoi pas. On pourrait aussi rétorquer en disant que la version dans l'url permet d'immortaliser une action sur une ressource à un instant T... Bref en réalité, on verra surtout que la méthode des entêtes HTTP avec DRF est beaucoup plus simple à maintenir que les méthodes de versioning d'url !

Quel numérotation des versions ?

Comme toujours, il n'y a pas vraiment de règles à suivre. Dans le doute, prenez exemple sur les plus grands. Par exemple, la procédure de mise à jour de Django utilise la semantic versioning. En gros, c'est <MAJEURE>.<MINEURE>.<PATCH>. Mais comme vous allez recourir au versioning le moins possible, vos versions ne devraient pas dépasser le niveau des versions majeures. Effectivement, un bug fix ne va (doit) pas impacter un changement côté client. Enfin...sauf si vos clients exploitent ce bug ! Ça s'appelle un trou de sécurité je crois. On vous a parlé de nos audits et formations de sécurité :p ?

Quel comportement par défaut ?

Demandez (obligez) à vos clients de toujours fournir la version souhaitée. Pas de version, pas de contrôle. Pas de contrôle, pas de palais. Pas de palais...pas de palais ! Maintenant que j'ai toute votre attention : il ne doit pas y avoir de version par défaut.

Pourquoi ? Admettons que la version par défaut est la 1.x. Des clients viennent sans fournir de version. Ils sont donc implicitement en 1.x. Demain, vous devez passer en 2.x par défaut parce que ça vous saoule de maintenir la 1.x qui est toute pourrie. Vous prévenez les derniers clients retardataires que la version ferme ses portes car vous êtes prévoyant et bien intentionné. Grand moment d'émotion, la bascule est faite. Seulement voilà... Un client un peu distrait n'a pas mis à jour son code source et continu d'interroger l'API en pensant que la signature de sortie est toujours une chaîne (1.x). Sauf que maintenant, l'API retourne un objet (2.x). Et paf, c'est le drame ! Alors que si le client avait été obligé de fournir la version souhaitée, certes il aurait continué de demander la version 1.x, mais il se serait mangé des réponses 4XX (du genre "eh mec réveille-toi, on est en 3.0 maintenant !"). Ça ne marche pas mieux mais au moins, le message est clair. On ne le dira jamais assez : explicit is better.

Conclusion

Le versioning est inévitable et idéalement, il doit être appliqué dès le début d'un projet en forçant les clients à fournir une version. Le choix de la méthode est délicat et dépend énormément du contexte. Néanmoins, il faut recourir au versioning le moins possible et assumer les changements côté serveur au maximum.

Maintenant que l'on a répondu à ces questions générales, on peut enfin aborder l'intégration du versioning de DRF dans un prochain article.

ABONNEZ-VOUS À LA NEWSLETTER !
Voir aussi
Formation Django initiation à Toulouse du 13 au 15 mars Formation Django initiation à Toulouse du 13 au 15 mars 26/01/2017

Entrez de plain-pied dans l'univers de Django aux côtés de développeurs ayant une expérience de ...

Alone in the cloud Alone in the cloud 14/12/2016

Thoughts while helping my brother-in-law

Retour sur la PyConFr 2016 Retour sur la PyConFr 2016 18/10/2016

Nous étions présents à Rennes pour PyConFr 2016. Voici notre compte-rendu à chaud.

Wagtail: How to use the Page model and its manager (part 2) Wagtail: How to use the Page model and its manager (part 2) 08/08/2016

The Page model has several methods specific to Wagtail. This is also the case of its manager. We ...

Wagtail : How to make your own content type models (part 1) Wagtail : How to make your own content type models (part 1) 29/07/2016

We are used to initialize our CMS directly from a web interface, often including lots of complex ...