Beetje Coderen

Les 2 · Module 1 · API-basis

REST-ontwerp: resources, methodes en statuscodes

Ontwerp de URL's van Winkel.Api volgens REST: resources als zelfstandige naamwoorden, de juiste HTTP-methode, idempotentie, statuscodes en versiebeheer.

⏱ 60–75 minuten leestijd + oefenen

Illustratie: een browservenster stuurt via pijlen verzoeken naar een server, het ontwerp van een REST-API

🎯 Na deze les kun je

  • resources en URL's ontwerpen met zelfstandige naamwoorden
  • de juiste HTTP-methode kiezen en uitleggen wat veilig en idempotent betekent
  • de passende statuscode kiezen voor succes en fouten
  • een versiestrategie voor je API kiezen

Een API is een contract: andere programma's bouwen erop, en elke wijziging kan ze breken. Een goed ontwerp maakt je API voorspelbaar: wie één endpoint kent, kan de rest raden. REST is geen standaard maar een set afspraken die bijna elke openbare API volgt. In deze les ontwerp je de URL's voor Winkel.Api, voordat je verder bouwt.

Resources, geen werkwoorden

Een URL wijst een ding aan (een resource), de HTTP-methode zegt wat je ermee doet.

Slecht Goed
GET /haalProductenOp GET /producten
POST /maakProduct POST /producten
POST /verwijderProduct?id=3 DELETE /producten/3
GET /producten/3/getReviews GET /producten/3/reviews

Het ontwerp voor de winkel:

/producten                    collectie
/producten/{id}               één product
/producten/{id}/reviews       reviews van een product (sub-resource)
/categorieen
/bestellingen
/bestellingen/{id}/regels

Kies één taal en blijf erbij (hier Nederlands), meervoud voor collecties, kleine letters, koppeltekens.

De methodes

Methode Doel Veilig? Idempotent?
GET ophalen ja ja
POST nieuw item in collectie, of een actie nee nee
PUT item volledig vervangen nee ja
PATCH item deels wijzigen nee niet per se
DELETE verwijderen nee ja
  • Veilig: verandert niets op de server. Een GET mag nooit iets wijzigen (zoekmachines en prefetch volgen links).
  • Idempotent: twee keer uitvoeren heeft hetzelfde effect als één keer. Belangrijk bij netwerkproblemen: een client mag een PUT of DELETE gerust herhalen.

Voorspel eerst: een client stuurt POST /bestellingen, krijgt een timeout, en probeert het opnieuw. Wat kan er misgaan, en waarom heeft PUT dat probleem niet?

Uitkomst

Misschien was de eerste POST wél aangekomen: dan heeft de klant twee bestellingen. POST is niet idempotent. Een PUT /producten/3 twee keer zet het product twee keer op dezelfde waarde, met hetzelfde resultaat. Voor POST gebruiken API's daarom een Idempotency-Key-header: de server onthoudt de sleutel en voert een herhaling niet nog eens uit (les 13).

Statuscodes

Code Gebruik
200 OK gelukt, met inhoud
201 Created aangemaakt; Location wijst naar het nieuwe item
204 No Content gelukt, geen inhoud (DELETE, soms PUT)
400 Bad Request invoer klopt niet (validatie, les 5)
401 Unauthorized niet ingelogd / geen geldige token
403 Forbidden wel ingelogd, geen recht
404 Not Found bestaat niet
409 Conflict botst met de huidige toestand (dubbele naam, oude versie)
422 Unprocessable Entity syntaxis goed, inhoud niet verwerkbaar (sommige API's)
500 Internal Server Error fout in jouw code; nooit bewust teruggeven

Een fout geef je niet terug als 200 met {"succes": false}: dan kunnen clients, proxies en monitoring het verschil niet zien.

Filteren, sorteren en pagineren via de query

GET /producten?categorie=toetsenborden&maxPrijs=50&sorteer=-prijs&pagina=2&perPagina=20

Het pad wijst de resource aan; de query bepaalt welk deel en in welke volgorde. Uitwerking in les 12.

Versies

Je API verandert. Een brekende wijziging (veld hernoemen, verplicht veld toevoegen) mag bestaande clients niet stukmaken.

  • URL-versie: /v1/producten – simpel en zichtbaar; de meest gebruikte keuze.
  • Header-versie: Api-Version: 2 – nettere URL's, lastiger te testen.

Toevoegen (een nieuw optioneel veld, een nieuw endpoint) is niet brekend. Verwijderen of hernoemen wel.

var v1 = app.MapGroup("/v1");
v1.MapGet("/producten", ...);

Typische fouten

  • Werkwoorden in URL's en dus voor elke actie een nieuw endpoint.
  • GET die iets wijzigt, bijvoorbeeld GET /producten/3/verwijder.
  • Overal 200 met een foutveld, of 500 voor gewone invoerfouten.

Oefenen

  1. Ontwerp de endpoints voor reviews: lijst per product, toevoegen, eigen review wijzigen en verwijderen. Geef methode, URL en statuscodes.
  2. Hoe zou je "bestelling annuleren" modelleren zonder werkwoord in de URL? Geef twee opties.
  3. Zet de endpoints uit les 1 in een groep /v1 en voeg de juiste statuscodes toe.
  4. Welke van deze wijzigingen zijn brekend? (a) veld omschrijving toevoegen, (b) naam hernoemen naar titel, (c) nieuw endpoint /v1/categorieen, (d) prijs van getal naar string.
Modelantwoord oefening 1
Methode + URL Succes Fouten
GET /v1/producten/{id}/reviews 200 404 (product)
POST /v1/producten/{id}/reviews 201 + Location 400, 401, 404, 409 (al een review)
PUT /v1/reviews/{id} 200 of 204 400, 401, 403 (niet van jou), 404
DELETE /v1/reviews/{id} 204 401, 403, 404

Eén review heeft een eigen URL (/reviews/{id}): je hoeft het product niet te kennen om hem te wijzigen.

Modelantwoord oefening 2

(1) PATCH /v1/bestellingen/{id} met {"status": "geannuleerd"}: de status is een eigenschap. (2) POST /v1/bestellingen/{id}/annulering: een annulering is een eigen resource (met datum, reden). Beide zijn gangbaar; optie 2 is duidelijker als annuleren regels heeft.

Modelantwoord oefening 3
var v1 = app.MapGroup("/v1/producten");
v1.MapGet("/", () => producten);
v1.MapGet("/{id:int}", (int id) => producten.FirstOrDefault(p => p.Id == id) is { } p ? Results.Ok(p) : Results.NotFound());
v1.MapPost("/", (Product nieuw) => producten.Any(p => p.Id == nieuw.Id)
    ? Results.Conflict()
    : Results.Created($"/v1/producten/{nieuw.Id}", Voeg(nieuw)));
Modelantwoord oefening 4

(a) niet brekend, (b) brekend, (c) niet brekend, (d) brekend: clients die een getal verwachten, gaan stuk.

Lokaal oefenen

Geen live lab. Bekijk ter vergelijking de documentatie van een openbare API (bijvoorbeeld die van GitHub of Mollie) en zoek de patronen uit deze les.

Samenvatting

  • URL's zijn zelfstandige naamwoorden; de methode is het werkwoord.
  • GET is veilig; PUT en DELETE zijn idempotent; POST niet.
  • Gebruik statuscodes eerlijk, filter via de query, versie via /v1.
  • Volgende les: minimal API's of controllers?

📝 Quiz: REST-ontwerp: resources, methodes en statuscodes

5 vragen · 70% nodig om te slagen

1. Welke URL past bij REST om product 3 te verwijderen?
2. Wat betekent idempotent?
3. Welke code geef je bij een dubbele productnaam?
4. Is het toevoegen van een optioneel veld een brekende wijziging?
5. Waarom geen 200 met {"succes": false}?