Systemen worden pas echt waardevol wanneer ze verbonden zijn. Zo kunnen ze input van andere systemen meenemen, processen helpen automatiseren en kun je hun data analyseren.

In een vorige blogpost lieten we je zien hoe je hier rekening mee kunt houden als je wilt zorgen dat een systeem dat je aanschaft goed ontsluitbaar is. Maar als je zelf systemen bouwt die je hierop wilt inrichten geeft dit ook vragen: hoe werken deze en hoe maak je dit proces zo vlekkeloos mogelijk? Daarover wil ik jullie wat verder gaan uitleggen in deze blogpost.

Hoe werkt een API?

De manier waarop we systemen verbinden is via API’s, oftewel Application Programming Interfaces. Deze bestaan uit een specificatie, bij wijze van afspraak tussen producent en consument van data. Zo’n afspraak kan dan van beide kanten geïmplementeerd worden. Je spreekt dan ook wel van API servers en API clients.

In de populaire REST standaard kunnen diensten over het internet praten via Hypertext Transfer Protocol (HTTP), waar ze elkaar aanspreken met een verzoek bestaande uit een URL (of breder: URI) samen met headers, een verb (zoals GET, POST) en afhankelijk daarvan verdere data, vervolgens beantwoordt met een respons eveneens voorzien van headers samen met een voor machines handige statuscode, zoals 200 (OK) of 404 (Not Found). REST verzoeken worden niet via sessies onthouden, dus elk verzoek dient opnieuw te authenticeren, meestal via de Open Authorization (OAuth) standaard.

Voor de uitwisseling van data zijn er verschillende formaten. Sinds de opkomst van de moderne webbrowser is de populairste optie hierin JSON geworden, oftewel JavaScript Object Notation. Waar deze robuuster gespecificeerd is dan spreadsheet-formaat Comma-Separated Values (CSV), zitten machine-leesbare validatieschema’s hier niet ingebakken, waar dit wel de norm was bij het op XML gebaseerd SOAP, die populair was in de Javawereld.

API’s omschrijven in schema’s

Specificeren van formaten in JSON is desalniettemin mogelijk via JSON Schema, waar je kunt aangeven wat voor object keys je gebruikt en welke data types daarachter schuilen. API responses worden hiermee omschreven in OpenAPI (voorheen Swagger), waarmee je zo kunt omschrijven wat voor antwoorden je (met welke statuscode) mag verwachten op elk API endpoint, bestaande uit zo’n HTTP verb bij een URL-pad en hoe die verzoeken daaraan gestructureerd worden.

Het handige hiervan is dat, doordat dit machine-leesbaar is, hier ook veel omheen gebouwd is. Zo kun je laten controleren of een respons wel klopt met dit afgesproken formaat, interactieve documentatie genereren waar een gebruiker verzoeken aan je API zelfs meteen kan uitproberen, maar ook voorbeeld data- of (web) formulieren kan maken die data in jouw formaat leveren, of zelfs code kan genereren voor databaseschema’s, voor zulke API clients, servers – of omgekeerd.

Nu is nog niet alles in deze specificaties gestandaardiseerd en moet je bijvoorbeeld zelf nog duidelijk maken hoe frequent je API aangeroepen mag worden (rate limits) of hoe verzoeken een groter resultaat in losse pagina’s opknippen. Zulke pagination wordt vaak opgevangen via een ‘envelop’, oftewel een standaard wrapper voor je data met een plek voor dit soort metadata. Rate limits kun je communiceren via HTTP headers, een robots.txt bestand of documentatie.

Ook houd je keuze over de URL-structuur, al is het gebruikelijk om filters en pagination via query parameters af te handelen, zoals /invoices/?paid=true&limit=50&offset=100 voor de betaalde facturen van de 101ste tot 150ste (binnen je standaard sorteer-methode).

ID-nummers die naar een vast object verwijzen, kunnen dan in URL query parameters zoals /clients/123/invoices/456 voor factuur 456 van klant 123. Dit is ook meteen handig voor je Search Engine Optimization (SEO), want zoekmachines weten zo dat deze laatste bijvoorbeeld wel los zinnig is om te indexeren, het eerdere voorbeeld waarschijnlijk niet.

Conclusie en tips

Ondanks beperkingen is het dus handig om je API in een OpenAPI schema te omschrijven, omdat dit veel van de randzaken makkelijker maakt zowel voor de producent als voor de consument van de data. Nu kan het daarbij makkelijk zijn om details te vergeten, bijvoorbeeld dat een veld ook missende data kan hebben (in JSON: null) of om zo’n schema bij te werken wanneer er een veld bijkomt. Hiervoor kan het bijvoorbeeld handig zijn om een van de bestaande implementaties rondom datavalidatie via OpenAPI of JSON Schema toe te passen, zodat je vanzelf kunt laten controleren of wat je krijgt (en wat je teruggeeft) nog met de beschreven afspraken kloppen.

Over de tijd is het natuurlijk ook te verwachten dat zulke afspraken evolueren. Het is dan handig om aanpassingen waar mogelijk compatibel te houden met vorige versies, of bij grote wijzigingen nieuwe versies op een nieuwe URL te plaatsen, zoals /v2/invoices/. Voor consumenten die graag op de hoogte willen blijven van wat hoe, wanneer, en waarom precies verandert, is het tot slot fijn om een plek te hebben waar ze zulke updates kunnen bijhouden. Zo kun je via versiebeheer systemen zoals Git je OpenAPI schema bijhouden en publiceren op websites als Github of Codeberg, waar ontwikkelaars kunnen kiezen om zulke updates te volgen en verschillen te vergelijken.

Wil je meer info over datagedreven werken? Blijf onze blog volgen voor nieuwe posts!