Een veelal onbegrepen onderdeel van REST is de rol van media types. In deze post ga ik iets dieper in op deze media types en leg ik uit waarom een REST API waarvoor geen media types gedefinieerd zijn nooit een REST API kan zijn.
Om aan te geven hoeveel verwarring er is over de rol van media types in REST APIs: een van mijn helden, Fowler, zegt bijvoorbeeld dat het prima mogelijk is om een REST API aan te bieden zonder dat je gebruikt maakt van media types, terwijl Fielding juist zegt dat het gebruik van media types een voorwaarde is voor REST. Een van de redenen voor deze verwarring is dat REST verschillende dingen voor verschillende mensen betekent. Voor sommigen betekent het gebruik van HTTP verbs in je API, voor anderen betekent het het gebruik van JSON als data-uitwisselings formaat, voor weer anderen betekent dat je de client en server kunnen onderhandelen over het uitwisselingsformaat. Voor Fielding betekent het een architectuurstijl waarmee je applicaties op internet-schaal kunt bouwen. Hierbij bedoelen we met schaal niet (alleen) performance, maar ook de uitdagingen die ontstaan doordat er meerdere organisaties en mensen betrokken zijn. Nu heeft Fielding de term REST geintroduceerd/gedefinieerd dus als je je API RESTful wilt noemen, zul je wat mij betreft moeten voldoen aan de eisen die hij daarvoor gezet heeft.
Als je dit standpunt aanbrengt in discussies hoor ik vaak het argument “dat dat wel erg puristisch is”. Nu is het opzich waar dat purisme in softwareontwikkeling niet altijd behulpzaam is, maar te vaak wordt dat argument gebruikt om af te wijken van de standaarden. Een veel voorkomend voorbeeld is dat organisatisch die starten met SCRUM oid de methode iets aanpassen “omdat dat beter past bij de organisatie”. De holistische aard van dergelijke processen wordt daarbij dan genegeerd, en uiteraard faalt de adoptie van het proces. Ik kan hier nog even over doorgaan, maar dat is voor een latere post.
Eenzelfde situatie ontstaat bij REST. Als je het belang van media types niet inziet, gebruik je REST waarschijnlijk voor de verkeerde reden. Voor Fielding is REST een architectuurstyle die gebruikt kan worden voor “distributed hypermedia systems” die qua levensduur gebruikt worden “on the scale of decades”. De eerste vraag die je moet stellen als je een RESTful API wilt aanbieden is of je een dergelijk systeem gaat bouwen. Waarschijnlijk niet. Veel van de APIs die ik tegenkom zijn voor het aanbieden van generieke, goed gedefinieerde diensten aan externe partijen. Denk hierbij aan koppelingen met e-mail en payment providers, weerdiensten, reserveringssystemen, etc. Dit zijn over het algemeen een paar calls die een zeer specifieke functionaltieit aanbieden en die qua interface ook zeer stabiel zijn. RPC is zeer geschikt voor dergelijke koppeling. De constraints van REST zijn daarentegen overdreven voor dergelijke calls, voornamelijk omdat het gebruik van caching/intermediates voor dergelijke calls vaak niet eens wenselijk is en er nauw tevens nauw contact zal zijn tussen leverancier en consumer, waardoor individual evolvability van veel minder belang is. En dit zijn juist de twee grote voordelen van REST.
Maargoed, stel dat je nu toch hebt besloten om een RESTful API aan te bieden, kan dat dan kwaad? Het antwoord is zonder meer: Ja, het kan kwaad. En wel om de reden dat een REST API je veel meer development effort (en dus geld) zal kosten, dan een vergelijkbare API via bijvoorbeeld SOAP. Waarom? Omdat SOAP juist een standaard is voor het doen van RPCs. Als de functionaliteit die je aan wilt bieden dus veel weg heeft van RPC, is dit een zeer natuurlijke oplossing. Het enige wat je zult moeten documenteren is de semantiek van je API. Wat betekenen de nouns en de operaties, maar niet hoe er gepraat moet worden tussen client en server en hoe fundamentele data structuren er op de lijn uit zien.
Omdat SOAP een standaard is is er bovendien veel tooling en support op de markt. Met behulp van WSDLs en IDEs is het zeer eenvoudig om een koppeling te bouwen tussen een SOAP client/server. Visual Studio kan bijvoorbeeld uit jouw service class/interface een WSDL genereren die vervolgens door een PHP client geconsumed kunnen worden, waarbij je in 10 minuten een werkende oplossing hebt. Dit in contrast met REST, daar zul veel meer moeten documenteren, namelijk niet alleen de semantiek, maar ook hoe je door de API heen navigeert, en wat voor operaties er mogelijk zijn.
Er wordt soms gedacht dat in een REST API de HTTP verbs en status codes al een vooraf gedefinieerde betekenis hebben voor elke willekeurige URI/resource. Dit wordt ook vaak als voordeel aangehaald, want “elke client die HTTP spreekt kan met de API communiceren”. Als je er een beetje over nadenkt, zul je echter al snel tot de conclusie komen dat dit niet waar kan zijn. Hoe kun je nu met iets praten waarvan je niet weet wat het is, wat het kan of wat het doet. Het onderscheid ligt ‘m in horen vs verstaan. De client en server zullen elkaar wel horen, maar elkaar niet verstaan. Om elkaar ook te verstaan heb je meer informatie nodig. De toegevoegde waarde van het gebruik van uniforme verbs en status codes is dat intermediaries weten hoe ze de requests/result moeten behandelen, bijvoorbeeld dat een GET safe is, maar een POST niet. En dat een 4xx return code een client fout betekent. Overigens zijn deze definities onderdeel van HTTP, niet zozeer van REST. REST praat alleen over een “uniform interface” en het gebruik van de verbs en status codes in HTTP is hier een implementatie van.
En daarmee komen we bij de crux van deze post aan, namelijk dat je geen REST kunt doen zonder media types. De media types zijn namelijk de documentatie, en ook de enige documentatie van je API. Hierin staat hoe je door je API navigeert, wat de mogelijkheden zijn, en wat voor semantische betekenis alles heeft. Een client en server zullen daarom nooit iets gedaan krijgen zonder dat ze allebei het media type kennen. Als je het niet gelooft nodig ik je uit om bijvoorbeeld eens de AtomPub RFC er bij te pakken. Kijk bijvoorbeeld naar sectie 4.3 over de beschikbare verbs en wat ze doen, en sectie 4.4 dat de overige verbs geen betekenis hebben volgens die RFC. Ook Fielding zegt hier iets over in de context van HTML: “anchor elements with an href attribute create a hypertext link that, when selected, invokes a retrieval request (GET) on the URI corresponding to the CDATA-encoded href attribute.”.
Als er beslissingen gemaakt worden op basis van andere informatie, zoals bijvoorbeeld de URI structuur, dan wordt dit ook wel out-of-band informatie genoemd. Het gebruik van dergelijke informatie is niet RESTful, omdat de server onafhankelijk moet kunnen doorevolueren.
Als er in een API bijvoorbeeld gebruik van application/json of application/xml als Content-Type (zonder Link: <location>; rel=”profile”), dan heb je out-of-band informatie nodig om de informatie te interpreteren. Het probleem met deze types is namelijk dat deze niks zeggen over hun inhoud (behalve dat het geformateerd is als xml/json) en dus ook niet hoe het door de client geinterpreteerd moet worden. Ook hier wordt vaak de URI gebruikt als indicatie wat de data is, maar dat is dus niet RESTful.
REST is dus helaas geen silver bullet voor zelfdocumenterende APIs. Het geeft wel aan hoe je moet documenteren, namelijk met media types. Alle documentatie wordt vastgelegd in het media type, en als je deze niet aanbiedt is het dus onmogelijk om een RESTful API te bouwen. Het definieren van deze media types hoeft niet per se veel werk te zijn, maar het is altijd meer werk dan het documenteren van een SOAP API, omdat SOAP al een standaard biedt voor het aanroepen van acties en de representatie van well-known datatypes.
Dankzij deze documantie effort zal het bouwen van een REST API altijd meer werk zijn dan een corresponderende API in SOAP. Je zult dus een goede reden moeten hebben om dit te doen (“distributed hypermedia systems”, “on the scale of decades”). En als je die reden dan hebt, dan moet je ook de full monty gaan, dus inclusief media types (sterker nog, daar moet je mee beginnen), om die voordelen ook daadwerkelijk te bereiken. Doe je dit niet, dan zul je als resultaat een RPC API hebben, waar niemand standaard aan kan koppelen en je heel veel werk gedaan hebt wat ook al door frameworks/libraries opgelost wordt, namelijk RPC.
Het moraal van dit verhaal is dat hoewel het tegenwoordig hot is om RESTful APIs te bouwen, het verstandig is om hier terughoudend mee te zijn. De voordelen die het biedt zijn namelijk lang niet altijd nodig, terwijl het sowieso meer tijd, geld en energie gaat kosten. En dat is waarschijnlijk beter gespendeerd aan andere dingen.
PS. Als je niet aan SOAP wilt omdat je liever met JSON praat, zoals in de browser of op mobile devices, dan zijn er nog andere opties. Zo biedt WCF een JSON binding en zijn er SOAP to JSON proxies.