REST API-versionering: URL, header of query, wat past
6 min lezen
REST API-versionering is de manier waarop een API een aanroeper vertelt welk contract een verzoek gebruikt, zodat de server het contract kan veranderen zonder aanroepers te breken die nog niet zijn overgestapt. De versie kan op drie plekken staan: in het URL-pad (/v2/orders), in een request-header (Accept: application/vnd.acme.v2+json of een eigen Api-Version: 2026-10-01), of in een queryparameter (/orders?version=2). Het pad komt het meest voor omdat het overal zichtbaar is, de header is het netst voor de URL, en de querystring is de keuze waar de meeste teams spijt van krijgen. Wat past hangt af van wie jullie aanroepers zijn en hoe je ze over wijzigingen wilt informeren.
| Waar de versie staat | Voorbeeld | Zichtbaar voor | Standaard cachebaar | Gebruikt door |
|---|---|---|---|---|
| URL-pad | GET /v2/orders | Elke log, link, proxy en browser | Ja | Twitter, Shopify, de meeste publieke API’s |
| Eigen header | Api-Version: 2026-10-01 | Alleen clients die hem zetten | Met Vary: Api-Version | Stripe (Stripe-Version), GitHub (X-GitHub-Api-Version) |
| Accept-header | Accept: application/vnd.acme.v2+json | Alleen clients die hem zetten | Met Vary: Accept | GitHub-mediatypes, sommige enterprise-API’s |
| Queryparameter | GET /orders?api-version=2 | Logs en links, vermengd met filters | Ja, maar vervuilt cachesleutels | Azure REST API’s |
| Geen versie | GET /orders plus alleen additieve wijzigingen | Niemand | Ja | Interne en GraphQL-achtige API’s |
Wat zijn v1 en v2 in een REST-API?
v1 en v2 zijn labels voor twee versies van het API-contract: dezelfde resources, bereikbaar op verschillende paden, waarbij v2 velden mag hernoemen, types mag veranderen, endpoints mag laten vervallen of foutvormen mag wijzigen waar v1-aanroepers op vertrouwen. Een verzoek aan /v1/orders blijft de oude vorm krijgen zolang v1 wordt ondersteund; een verzoek aan /v2/orders krijgt de nieuwe. Het nummer is een belofte over compatibiliteit, en die belofte is wat het nuttig maakt. Het bij elke release ophogen maakt van het label ruis.
De meeste API’s hebben alleen een nieuwe hoofdversie nodig voor een wijziging die bestaande aanroepers breekt. Een veld, een nieuw endpoint of een nieuwe optionele parameter toevoegen kan onder de huidige versie, want een goed geschreven client negeert wat hij niet kent. Wanneer je een API versioneert, en wat je anders doet behandelt die beslissing; dit artikel gaat over waar de versie heen gaat bij REST API-versionering zodra je besloten hebt hem op te hogen.
Hoort REST API-versionering in de URL of in een header?
Zet hem in de URL als jullie aanroepers ook mensen zijn die verzoeken in een browser typen, voorbeelden uit de documentatie kopiëren of logs lezen; zet hem in een header als je één stabiele URL per resource wilt en jullie aanroepers allemaal SDK’s zijn of code die je zelf beheert. Het URL-pad is expliciet op een manier die niets anders is: een supportticket met de requestregel erin vertelt je al de versie. Een header moet op elk verzoek worden gezet, is onzichtbaar in een link en gaat makkelijk verloren bij een proxy, en daarom definiëren header-geversioneerde API’s altijd een standaard voor verzoeken die er geen sturen.
De headeraanpak heeft één echt voordeel naast nette URL’s: hij scheidt de identiteit van de resource van zijn representatie, en zo is HTTP ontworpen. RFC 9110 beschrijft content negotiation via Accept, en een mediatype als application/vnd.github.v3+json is een versie die zo wordt uitgedrukt. In de praktijk weegt de zuiverheid van het ontwerp minder zwaar dan of jullie aanroepers eraan denken de header te sturen.
Hoe werkt versionering op datum?
In plaats van v1 en v2 is de versie een datum, en elke breaking change krijgt een nieuwe datum. Een aanroeper pint de datum vast waartegen hij integreerde, meestal eenmalig op accountniveau, en houdt dat gedrag tot hij zelf besluit over te stappen. Stripes Stripe-Version-header en GitHubs X-GitHub-Api-Version werken allebei zo.
De afweging zit in de implementatie. Elke breaking change wordt een kleine vertaallaag die omzet tussen de vastgepinde datum en de huidige vorm, en de server stapelt ze: een verzoek dat twee jaar terug is vastgepind gaat door elke wijziging sindsdien. Dat is zwaarder om te bouwen dan een tweede kopie van de handlers, en het beloont een API die zelden verandert. Teams die elke maand een breaking change uitbrengen eindigen met tientallen vertalingen. Stripe API-versionering bekijkt het model vanaf de kant van de aanroeper, inclusief wat de changelog moet dragen.
Waarom wordt versionering in de querystring afgeraden?
Omdat het het contract vermengt met de filters. /orders?status=open&api-version=2 laat de versie lijken op een optionele verfijning in plaats van het ding dat de vorm van elk veld in het antwoord bepaalt. Het is ook de makkelijkste om per ongeluk te verliezen, omdat querystrings worden herbouwd door clientlibraries, gestript door sommige proxies en met de hand bewerkt door mensen. Microsofts eigen richtlijn voor REST API-ontwerp documenteert de queryparametervorm die Azure gebruikt, en het ecosysteem van gegenereerde SDK’s eromheen is wat dat werkbaar maakt; zonder die tooling krijgt een aanroeper die de parameter vergeet stilletjes de standaardversie.
Wat gebeurt er met de oude versie?
Die blijft draaien, met een datum waarop hij stopt. Het punt van versioneren is aanroepers op hun eigen schema laten overstappen, en dat is alleen waar als de oude versie lang genoeg beschikbaar blijft om dat te doen en ze te horen krijgen wanneer hij eindigt. Googles richtlijn voor API-versionering beschrijft de levenscyclus die het voor zijn eigen API’s hanteert: een stabiele versie wordt ondersteund tot een gepubliceerde deprecatiedatum, met de vervanger beschikbaar gedurende het hele deprecatievenster.
De aankondiging is het deel dat de meeste teams te mager bouwen. Een nieuw pad of een nieuwe header is alleen nuttig voor aanroepers die weten dat hij bestaat, weten wat er veranderde en weten wanneer v1 verdwijnt. Dat zijn drie feiten, en elk heeft een plek nodig: een gedateerde entry in de API-changelog met de verschillen op veldniveau, een deprecatiebericht dat de einddatum noemt, en een migratiegids die elke wijziging doorloopt voor een aanroeper die nog op de oude versie zit. Een API-versie uitfaseren behandelt de laatste stap, inclusief de Sunset-header waarmee een client de datum op elk antwoord kan ontdekken.
Kun je REST API-versionering helemaal vermijden?
Ja, zolang elke wijziging additief is. Voeg velden toe, verwijder of hernoem ze nooit; voeg endpoints toe, verander nooit wat een bestaand endpoint teruggeeft; accepteer nieuwe optionele parameters, verander nooit de betekenis van een oude. Veel interne API’s draaien jaren op die regel alleen, en het is de regel die GraphQL-schema’s van nature volgen. De prijs is een contract dat alleen maar groeit, met gedeprecieerde velden die moeten blijven omdat iemand, ergens, ze nog leest.
Wanneer een additieve wijziging onmogelijk is, is de versie het gereedschap. De keuze tussen pad, header en datum bepaalt wat aanroepers zien en hoe de wijziging wordt aangekondigd; wat constant blijft is dat de wijziging ergens moet worden opgeschreven waar een aanroeper het leest. Voor een API met externe aanroepers is die plek de changelog, en het artikel over breaking changes behandelt wat daaronder valt.
FAQ
Wat is de beste manier om een REST-API te versioneren? Een hoofdversie in het URL-pad wordt het breedst begrepen en is het moeilijkst fout te doen, wat het de standaard maakt voor een publieke API. Een headerversie houdt URL’s stabiel en past bij SDK-gedreven API’s; een gedateerde header past bij API’s die zelden veranderen en aanroepers gedrag willen laten vastpinnen.
Wat is v1 in een API-URL?
Het is de eerste hoofdversie van het API-contract. Verzoeken aan /v1/... krijgen de vorm beloofd die voor versie 1 is gedocumenteerd, ook nadat er een /v2/ bestaat met andere velden of ander gedrag.
Heb ik voor elke wijziging een nieuwe API-versie nodig? Nee. Additieve wijzigingen zoals nieuwe velden, endpoints en optionele parameters kunnen onder de bestaande versie worden uitgebracht. Alleen een wijziging die bestaande aanroepers breekt heeft een nieuwe nodig.
Hoelang moet een oude API-versie worden ondersteund? Lang genoeg om aanroepers te laten migreren, wat voor een publieke API meestal zes tot twaalf maanden na beschikbaarheid van de vervanger is, met de datum gepubliceerd bij de aankondiging en herhaald in het deprecatiebericht.
De technische beweringen in dit artikel zijn niet onafhankelijk gecontroleerd. Klopt er iets niet, laat het ons weten, dan corrigeren we het.