Direkt till innehållet

Designprinciper och versionshantering

Regionen har utvecklat flera olika Apier för olika informationsdomäner, men vi har försökt att i största möjliga mån följa detta designmönster. Då vårt mönster justerats över tid kan http-verben som används skilja sig åt mellan de olika Apierna, på senare tid har vi övergått till att enbart använda POST i våra Apier, därför kan det fortfarande finnas referenser till Querystring-parametrar som i det specifika Apiet istället är implementerat som en body-parameter. Namnen är dock lika.

Denna artikel ska läsas som bakgrund till den specifika Api-dokumentationen som du finner på Developer-portalen.

Inledning

Vi förespråkar pragmatisk Api design, vilket innebär att vi utgår ifrån vad det är vi försöker uppnå med vårt Api och se det ur en systemutvecklares perspektiv och att denne ska bli så framgångsrik som möjligt i användandet av Api-et. Enkelhet är den viktigaste principen i skapande av Api-er men att göra någon enkelt är ganska komplext. Där finns denna designhandbok till hjälp, att göra även den senare komplexiteten enkel.

Som exempel på dataentiteter i detta dokument kommer datorer och program att användas. En dator kan ha program installerat på sig och ett program kan vara installerat på flera datorer.

Språk och nomenklatur

Som språk i namngivning av variabler, resurser, domännamn m.m. så använder vi engelska. För nomenklatur så följer vi olika branschstandarder, för hälsoinformationssystem så innebär det HL7 FIHR.

I data som skickas och tas emot så kommer dock språket alltid att vara styrt av källdata. I de flesta fall så finns data på svenska.

Http-verb

I REST förekommer fem verb: GET, PUT, POST, PATCH och DELETE. Vi har dock valt att endast använda ett enda, nämligen POST.

I den första gallringen så inledde vi med att utesluta PUT och istället alltid använda PATCH. Skillnaden mellan PUT och PATCH är att PUT kräver att anropet alltid innehåller alla delar av data som ska ändras, även de delar som inte ändrats. PATCH å andra sidan kan innehålla endast de delar av data som förändrats.

I den andra gallringen av http-verb så vägde vi in säkerhetsaspekten och då i synnerhet GDPR. En vanlig GET kan se ut så här:

GET /Patients/121212-1212?diagnose=corona

I ovanstående exempel exponerar vi både patientens personnummer och dess diagnos. Informationen i URL-en kommer troligtvis att loggas på en webbserver och om den används i en webbläsare, även i webbläsarhistoriken. Samma mönster, med att känslig information kan finns i URL, återfinns även för verbet DELETE. Vi behöver alltså komma på ett sätt att dölja känslig information så att den inte lagras någonstans på vägen. Vi har därför bestämt att använda POST som ersättning för GET och DELETE, eftersom vi med hjälp av POST lägger den känsliga informationen i body. URL-en fastnar ofta i accessloggar och där vill vi undvika att exponera känslig data.

Verbet PATCH hade vi kunnat ha kvar men vi valde en strikt förenklad modell, där vi även ersätter PATCH till förmån för POST.

Substantiv och verb, resurser

En grundläggande regel är att det i URL-er endast får förekomma substantiv, inte verb:

POST /Computers/detail BODY: {id, params}

Returnerar ETT detaljobjekt + eventuella djupa fält

POST /Computers /list BODY: {listparam)}

Returnerar EN lista av listobjekt

URL-er ska alltså alltid skrivas med substantiv, plural, obestämd form. Namngivningen av resurserna ska vara konkreta snarare än abstrakta, allt för enkelheten. Namngivningen ska vara på svenska där å och ä ersätts med a och ö ersätts med o.

För att få dessa URL-er att utföra någon form av operation så använder vi:

/{parameter}

I ovanstående exempel använder vi detail och list för att få ett objekt respektive en lista av objekt.

De flesta resurser har relationer till andra resurser. För att ta reda på vilka program som installerats på en dator eller för att lägga till ett program till en dator så detta användas:

POST /Computers/detail/Software BODY: {id, params}

Ovanstående exempel är uppbyggda enligt: /Resurs/Parameter/Resurs

Det är lockande att bygga ytterligare nivåer där men undvik att använda fler nivåer än två.

I och med att vi valt att utesluta fyra av fem http-verb så följer här en lista över hur det kvarvarande verbet ska användas för att täcka alla behov, läsa, skriva, uppdatera och radera:

Hämta detalj:

POST /Computers/detail BODY: {id, params}

Returnerar ETT detaljobjekt + eventuella djupa fält

Hämta lista:

POST /Computers /list BODY: {listparam)}

Returnerar EN lista av listobjekt

Skapa ny post:

POST /Computers BODY {resourceobject}

Uppdatera post:

POST /Computers/updated BODY {id, patchobject}

Radera post:

POST /Computers/deleted BODY {id}

Innehållshantering

Alla anrop och svar ska vara i JSON-format:

Content-Type: application/json

eller

Accept: application/json

Om inget specificeras så är det JSON som returneras. För den som vill ha XML eller annat så finns det parsers i de flesta programmeringsspråk

Notation

Properties

Notationen camelCase ska användas för variabelnamn i JSON. Växlingen från gemen bokstav till versal sätts där det, i engelska språket, normalt sett finns ett mellanslag. Ordet computers skrivs alltså som ”computers” med camelCase.

Resurser och domäner

Notationen PascalCase ska användas för resurser och domäner. Växlingen från gemen bokstav till versal sätts där det, i engelska språket, normalt sett finns ett mellanslag. Ordet computers skrivs alltså som ”Computers” med PascalCase.

Dataformat

Decimal- och tusentalsavgränsare

Som decimalavgränsaren använder vi en punkt (.) och tusentalsavgränsare utesluter vi helt. Genom att vara konsekventa så hänför vi eventuella konverteringar till konsumerande system. Decimalavgränsaren är dock mycket viktig eftersom flera lokaliseringar använder andra tecken och dessa tecken ibland förväxlas med andra avgränsare.

Datum och tid

Vi följer den internationella standarden, ISO 8601, vilken också rekommenderas av W3C. Det innebär att datum och tid representeras på detta sätt:

YYYY-MM-DDThh:mm:ssTZD 

1997-07-16T19:20:30+01:00

Exemplet ovan är den standard vi utgår från. I undantagsfall kan en högre eller lägre tidsprecision vara önskvärd och i de fallen kan sekundangivelsen antingen utökas med decimaltal på sekundnivå, alternativt så kan sekundangivelsen utelämnas.

Dokumentstruktur

Rotobjektet i ett JSON meddelande är alltid ett JSON-objekt. I detta rotobjekt finns alltid två av dessa medlemmar, som i sig är JSON-objekt.

meta: Innehåller metadata kring meddelandet. Samtliga egenskaper i meta-medlemmen är frivilliga eftersom olika datakällor har olika möjligheter.
data: Innehåller primärt data i meddelandet.
errors: Innehåller eventuella felmeddelanden

Medlemmarna data och errors finns aldrig representerade samtidigt.

Ett exempel utan felmeddelande:

{

”meta”: {

”self”: ”Computers/list?limit=10&offset=0”

”next”: ” Computers/list?limit=10&offset=10”,

”previous”: null,

”limit”: 10,

”offset”: 0,

”itemcount”: 124,

”pagecount”: 7,

”currentpage”: 3

}

”data”: [

{object 1},

{object 2}

]

}

Ett exempel med felmeddelande:

{

”errors”:

{

”errorCode”: 1001

”developerMessage”: ”Something went wrong”

”userMessage”: ”Something went wrong”

}

}

Hantering av fel

För en utvecklare som ska använda ett Api så kan detta te sig som en svart låda. En god felhantering kan dock ge systemutvecklaren som ska använda våra Api-er en förståelse för hur Api-et fungerar. Den första principen är att använda standardiserade http-statuskoder men dessa är ganska många till antalet, ett knappt femtiotal. Vi har därför valt att koka ner dessa till tre ”summakoder”:

  1. Allt fungerade.
  2. Applikationen gjorde något fel – klient fel.
  3. Api-et gjorde något fel – serverfel.

Ovanstående tre ”summakoder” kan sedan expanderas till dessa sex egentliga http-statuskoder:

1. Allt fungerade.

200 – OK POST som gått bra.

2. Applikationen gjorde något fel – klient fel.

400 – Bad Request. Alla fel som t.ex. rör affärsregler.

401 – Unauthorized. Anropet är inte autentiserat.

403 – Forbidden. Anropet får inte komma åt resursen (med det verbet).

404 – Not found. Resursen finns inte.

3. Api-et gjorde något fel – serverfel.

500 – Internal Server Error

Vid felkoder, alltså andra än 200, så ska alltid ett developerMessage bifogas Innehållet i developerMessage bör bestå av en enkel beskrivning av vad som gått fel och, där det är lämpligt, även en beskrivning av lämplig åtgärd. Som namnet antyder så är ett developerMessage riktat till systemutvecklaren och språket i det ska vara på engelska. Det är systemutvecklarens ansvar att utifrån developerMessage skapa meddelanden som är lämpliga att presentera i ett gränssnitt för en slutanvändare.

Sortering

Sortering åstadkoms med nyckelordet ”sort”:

POST /Computers/list?sort=model asc,purchasedate desc

Sorteringsordning:

asc - Stigande sortering.

desc - Fallande sortering.

Det är inte obligatoriskt för alla Api-er att stödja sortering. Observera att en del Apier tar dessa parametrar i bodyn istället för i Querystringen. Det framgår i så fall i den specifika Api-dokumentationen.

Filtrering

Filtrering är en viktig funktion för Api-er, särskilt vid stora mängder data. Nyckelordet för filtrering är ”filter”:

POST /Computers/list?filter=type=’server’,manufacturer!=’dell%’,memorysize>50

Om mer än ett uttryck specificerats så kombineras detta med AND.

De operatorer som är tillåtna är följande:

= Är lika med, antingen med exakt matchning eller med LIKE via ett eller två %-tecken.
!= Icke lika med.
> Större än.
< Större än, lika med.
>= Mindre än.
<= Mindre än, lika med.

Det är inte obligatoriskt för alla Api-er att stödja filtrering. Observera att en del Apier tar dessa parametrar i bodyn istället för i Querystringen. Det framgår i så fall i den specifika Api-dokumentationen.

Paginering och antalsbegränsning

För att enkelt kunna göra en begränsning i datamängd som returneras från Api-er så används nyckelorden ”limit” och ”offset”. Alla svar från Api-et ska innehålla uppgift om vilken limit och offset som returnerats.

Det är viktigt att den som konsumerar datakällan kan få reda på hur många poster som finns totalt för att kunna sätta rätt värden på limit och offset.

Observera att limit och offset har ett starkt beroende till både sortering och filtrering.

Nyckelorden limit och offset sätts samman i en Query string i vissa Apier och i anropsobjetket (i bodyn) i vissa Apier:

?limit=25&offset=50

Ovanstående exempel innebär att 25 poster hämtas med början på post nummer 50 i ett nollbaserat index.

Om anropet inte innehåller limit och offset så sätts alltid offset till 0. Defaultvärdet för offset kan skilja sig från fall till fall men bör sättas eftertänksamt och med hänsynstagande till datakällan. Ett lämpligt värde kan vara 200.

Begäran om att erhålla samtliga poster i en datakälla anger limit till -1 och offset till 0:

?limit=-1&offset=0

Här kan Api-et, beroende på datakällans innehåll, returnera en 400 kod och felmeddelande om att data set innehåller för många poster i svaret. I Api-et så måste det alltså alltid finnas en hantering av maximalt antal poster i svaret.

Om antalet poster i datakällan understiger det begärda så ska inget felmeddelande ges utan det mindre antalet returneras då. Exempelvis om datakällan innehåller 20 poster och begäran är limit=25 och offset=50 så ska data i retur vara samtliga poster och limit=-1 och offset=0.

Det är inte obligatoriskt för alla Api-er att stödja limit och offset. Observera att en del Apier tar dessa parametrar i bodyn istället för i Querystringen. Det framgår i så fall i den specifika Api-dokumentationen.

Fältbegränsning

Ibland är det viktigt att begränsa antalet fält som returneras, inte minst för att begränsa bandbreddsutnyttjandet. Nyckelordet för det ändamålet är fields:

POST /Computers/list?fields=type,manufacturer,memorysize,serialnumber

Här anges helt enkelt namnet på de fält som ska returneras från datakällan.

Ett Api kan även stödja fältbegränsning av djupa fält. Ett djupt fält är ett fält var innehåll antingen är ytterligare ett objekt eller en lista av objekt. I det fallet ska de djupa fälten endast populeras om de är inkluderade i fields.

POST /Computers/list?fields=type,software,os

I exemplet ovan kommer fälten software och os att populeras med alla fält. Det går även att selektera vilka fält i det djupa objektet som ska populeras genom punktnotation.

POST /Computers/list?fields=type,software.name

I Exemplet ovan kommer endast fältet name populeras i fältet software.

Det är inte obligatoriskt för alla Api-er att stödja fields. Observera att en del Apier tar dessa parametrar i bodyn istället för i Querystringen. Det framgår i så fall i den specifika Api-dokumentationen.

Fältbegränsning djupa fält

En del resurser kan innehålla djupa fält, dvs fält som är ytterligare ett objekt med egna egenskaper. I vissa fall kan även fältbegränsning stödjas i dessa fält.

I exemplet nedan räknas software och os som djupa fält. Dessa kommer inte returneras alls om de inte är inkluderade i fields-parametern.

{

"id": 0,

"name": "string",

"description": "string",

"location": "string",

"software": [

{

"softwareId": 0,

"status": "Unknown",

"name": "string",

"description": "string",

"version": "string"

}

],

"os": {

"id": 0,

"name": "string",

"description": "string",

"version": "string"

},

"createdDate": "2019-03-10T15:06:16.016Z",

"createdBy": "string",

"updatedDate": "2019-03-10T15:06:16.016Z",

"updatedBy": "string"

}

POST /Computers/list?fields=software,os.name

Kommer att returnera hela software-objektet, men bara name i os-objektet.

Senast ändrad: