Wie versioniert man öffentliche Web-APIs?
(lobste.rs)- Wenn eine öffentliche Web-API einen Namen wie Product API zusammen mit einem Pfad wie
/api/v1verwendet, können die semantische Version der API selbst und ihre Struktur auseinanderlaufen - Wenn
/v1/undmajor.minor.patchparallel verwendet werden, vermischen sich Routen und API-Vertrag, und die erste Zahl der semantischen Version wird in der URL fest verankert - Bei inkompatiblen Änderungen werden ein neuer Pfad und Reverse-Proxy-Routen nötig, wodurch Vertragsinformationen auf URL und Versionsnummer verteilt werden können
- Wenn gleichzeitig nachfolgende APIs entwickelt werden, ist die bestehende API faktisch an
v1gebunden, und bei späteren Breaking Changes wird die Bedeutung von Name und Pfad uneindeutig - Es geht um die Frage, welche bei der Versionierung öffentlicher Web-APIs immer wieder störenden Ansätze es gibt und nach welchen besseren Designprinzipien man stattdessen suchen sollte
Möchten Sie weitere kuratierte Tech-Themen erhalten?
Folgen Sie dem Telegram-Kanal. @GeekNewsDE
1 Kommentare
Lobste.rs-Meinungen
/v1/in der URL zu haben, ist tatsächlich einer der großen Vorteile. Denn bis man einen Endpunkt abschaltet, erzwingt es gegenüber den Nutzern, dass die API nicht kaputtgemacht wirdAndere Texte desselben Autors wie Evolving HTTP APIs geben nützliche Ratschläge
Grundsätzlich versieht man jede Route mit
/v1/,/v2/usw., um Breaking Changes zu kennzeichnen. Wenn es sich nicht um einen Standard handelt, der auf mehreren Hosts funktionieren soll, sondern um eine öffentlich betriebene API, gibt es wenig Grund für vollständiges Semantic VersioningSemantic Versioning existiert ja vor allem, damit andere Entwickler Abhängigkeiten mit gutem Gewissen aktualisieren können, ohne jedes Mal 20 Minuten Changelog zu lesen. Bei einer produktiven API können sich die Leute aber nicht aussuchen, wann sie eine neue Minor- oder Patch-Version übernehmen
Als Breaking Change würde ich alles ansehen, was ein dokumentiertes Verhalten verändert oder bestehende Clients kaputtmacht, die sich auf dokumentiertes Verhalten verlassen. Manche zählen auch Änderungen an undokumentiertem Verhalten als Breaking Change, aber das ist mit vielen Risiken verbunden
Google macht es so: AIP-185: API Versioning, AIP-180: Bacwards compatibility
Diese Designdokumente wirken auf mich ziemlich stark auf Googles Arbeitsweise zugeschnitten, aber ich habe sie beim Entwerfen von APIs als Referenz genutzt, und einige Ideen daraus waren sehr hilfreich
Generell sollte jede API meiner Meinung nach versuchen, Breaking Changes so weit wie möglich zu vermeiden. Wenn man zum Beispiel einen Attributnamen ändern will, ist es meist besser, den neuen Namen zusätzlich einzuführen, statt das bisherige Attribut zu entfernen
Allerdings ist auch der Ansatz von the people at Buttondown do it sauber. Dort werden Migrationen zwischen API-Versionen definiert, sodass Verbraucher ihre API-Version per Header festschreiben können, während der Anbieter Änderungen weiter vorantreibt
Man denkt sofort an „der neue Name hat immer Vorrang“, aber das kann scheitern, wenn ein Client einen Read-Modify-Write-Ablauf ausführt und eine bearbeitete Version eines serverseitig erzeugten Objekts zurückschickt. Der Client könnte nämlich nur das alte Attribut aktualisieren und das neue unverändert wieder mitsenden
Solche Transformationen ließen sich vermutlich auch auf Verhaltenszuordnungen anwenden, aber sofern ich nichts übersehen habe, wurde dieser Punkt dort nicht behandelt
Idealerweise nimmt man die Version in den Pfad auf, und neue Versionen sollten additiv sein. Dann kann eine ältere API-Version Anfragen nach den nötigen Ein-/Ausgabe-Transformationen an eine neuere API-Version weiterleiten
Wenn nach ein paar Jahren niemand mehr irgendeine alte Version verwendet, kann man sie entfernen, und der
/v1/-Pfad liefert dann einen FehlerIch habe früher ein wenig über API-Versionierung via Content Negotiation über den
Accept-Header gelesen. Falls das jemand in der Praxis gemacht hat, würde mich die Erfahrung interessierenNach meiner Erfahrung waren versionsbezogene Ansätze pro Ressource oder global am intuitivsten. Für die Ausphasung erscheint mir die Kombination aus dem
Deprecation-HTTP-Response-Header (RFC 9745) und später Antworten wie410 Gonefür alte Endpunkte als ein vernünftiger Weg, Clients zum Umstieg auf neue Versionen zu bewegenAußerdem würde mich wirklich interessieren, ob jemand schon einmal eine evolvierbare API gebaut hat. Also eine, die Anfragen an alte Versionen intern in Anfragen an eine neue API-Version übersetzt und die alte Version dann entfernt, sobald die Clients migriert sind oder genug Zeit vergangen ist