Fire designvalg i et REST-API for et sakssystem

Vi bygde et sakssystem med REST-API til internt bruk, der kunder, prosjekter og saker henger sammen. Fire valg vi tok tidlig, sparte oss for omskriving senere. Alle fire er billige å innføre og dyre å rette opp i ettertid.
1. Nest ressursene i URL-en

En sak tilhører et prosjekt, og et prosjekt tilhører en kunde. Speil hierarkiet i ruten. Flate ruter som /issues/42 blir tvetydige så snart du har mer enn ett prosjekt, fordi ruten ikke sier hvem som eier saken. Nestede ruter gjør eierskapet eksplisitt og lar deg autorisere på hvert nivå.
GET /customers/{customer}/projects/{project}/issuesUlempen er lengre URL-er. Til gjengjeld trenger du ikke hente kontekst fra query-parametere, og ruteren gir 404 når en sak ikke tilhører prosjektet i URL-en.
POST /customers/{customer}/projects/{project}/issues
GET /customers/{customer}/projects/{project}/issues/{issue}
2. Skill saksnummeret fra database-id-en
Brukerne vil ha et kort saksnummer per prosjekt. Prosjekt A har sak 1, 2 og 3, og prosjekt B har også sak 1, 2 og 3. issue_number er derfor ikke unikt på tvers av prosjekter. Bruk databasens id i URL-en og vis issue_number i grensesnittet.
{Blander du de to, kolliderer numrene på tvers av prosjekter, og oppslag treffer feil sak. Hold dem adskilt fra starten.
"id": 4821, // globalt unik, brukes i URL
"issue_number": 3, // vises til brukeren, unik per prosjekt
"title": "Feil i eksport"
}
3. Tving JSON på skrivende kall

Feiler en forespørsel og klienten ikke har bedt om JSON, svarer rammeverket ofte med en HTML-feilside. API-klienten får da markup i stedet for en feil den kan lese. Krev disse headerne på alle POST-, PUT- og DELETE-kall:
Accept: application/jsonMed
Content-Type: application/jsonAccept: application/json svarer rammeverket med JSON også ved valideringsfeil og 500. Uten den feiler parseren på HTML der den ventet et feilobjekt.
4. Bruk flate query-filtre
Lag ikke et filterspråk med nestede parametere før du trenger det. Flate filtre er lette å lese, dokumentere og cache.
GET /customers/1/projects/2/issues?status=openUnngå
GET /customers/1/projects/2/issues?status=open&priority=high?filter[status]=open i et internt system. Formatet krever mer parsing, dokumentasjon og klientkode enn det gir tilbake.
Svarformat
Svaret har en data-liste og en meta-blokk med paginering, slik at klienten alltid finner nyttelasten på samme sted.
{
"data": [ { "id": 4821, "issue_number": 3, "title": "..." } ],
"meta": { "current_page": 1, "last_page": 5, "per_page": 20, "total": 92 }
}

