- Upute za dizajn API-ja
API mora biti dizajniran tako da bude:
- jednostavan za uporabu
- jasan i razumljiv
- konzistentan
- siguran
- nadogradiv
Dizajn API-ja bi trebao polaziti od potreba korisnika. Prije svega treba identificirati što korisnik želi postići koristeći se uslugom, te koje akcije može poduzeti kako bi ostvario svoje ciljeve. API ne bi smio biti organiziran kao preslika strukture baze podataka nego prema mogućnostima koje su korisnicima potrebne.
Konzistentnost u API-ju je bitnija od ispravljanja neadekvatnih naziva resursa ili atributa. Jednom odabrani naziv potrebno je primjenjivati kroz cijeli API.
Kod imenovanja resursa, atributa i parametara treba se pridržavati sljedećih pravila:
📜 Pravilo | ✅ Primjer | ⛔ Izbjegavati |
---|---|---|
Nazivi resursa, atributa i parametara moraju biti na (američkom) engleskom jeziku. | /users /colors |
/korisnici /colours |
Nazivi resursa, atributa i parametara moraju biti kratki, jasni i ne preopćeniti. | /product |
/item |
Nazivi resursa moraju biti imenice. | /users |
/get-users |
Kolekcije moraju biti dostupne na URL-ovima s imenicom u množini. | /users |
/user |
Kada resurs nije kolekcija, tada se upotrebljava imenica u jednini. | /account |
/accounts |
Imena resursa moraju biti u kebab-case formatu. |
/academic-years /messaging-groups |
/academic_years /academicYears |
Imena atributa i parametara moraju biti u camelCase formatu. |
firstName lastName |
first_name FirstName |
Izbjegavati redundanciju u imenima resursa, atributa i parametara. | /users/{id}/groups |
/users/{id}/user-groups |
Kratice se pišu malim slovima, poput riječi. | id userId |
ID URL |
Za riječ koja se piše sa spojnicom (-) vrijede pravila kao da se radi o više riječi. | /real-time realTime |
/realtime realtime |
Boolean varijable moraju imati prefiks is , has , can , should , allow ili show . |
isActive hasChildren |
active children |
Oznake resursa su jedinstveni identifikator određenog resursa na API-ju i njihov naziv se formira spajanjem naziva kolekcija u URL-u, ali primjenom imenica u jednini i u formatu camelCase
. U slučaju da je samo dio URL-a dovoljan za stvaranje jedinstvene oznake i ta oznaka jasno predstavlja resurs, tada nije potrebno navoditi sve nazive kolekcija iz URL-a u oznaci resursa. Na primjer:
- za URL
/users/{id}
oznaka resursa jeuser
- za URL
/users/{id}/groups/{id}
oznaka resursa jeuserGroup
- za URL
/academic-years/{id}
oznaka resursa jeacademicYear
- za URL
/students/{id}/enrolled-courses/{id}
oznaka resursa jeenrolledCourse
(jer je jasno da se radi o podacima o upisu kolegija)
Za oznake skupine resursa upotrebljava se oznaka resursa u množini. Na primjer:
- za URL
/users
oznaka skupine resursa jeusers
- za URL
/users/{id}/groups
oznaka skupine resursa jeuserGroups
- za URL
/students/{id}/enrolled-courses
oznaka skupine resursa jeenrolledCourses
- za URL
/academic-years
oznaka skupine resursa jeacademicYears
Oznake resursa su korisne za jednostavno referenciranje resursa u dokumentaciji i logiranju, te omogućuju da svakome tko promatra odgovor odmah bude jasno o kojim podacima se radi.
Primjeri primjene oznaka resursa:
{
"data": {
"user": {
"id": "1234",
"name": "John Doe",
"email": ""
}
}
}
{
"data": {
"users": [
{
"id": "1234",
"name": "John Doe",
"email": ""
},
{
"id": "5678",
"name": "Jane Doe",
"email": ""
}
]
}
}
Sljedeće nazive potrebno je izbjegavati jer su preopćeniti:
- elements
- entries
- instances
- items
- objects
- resources
- types
- values
Oni se mogu u nekim slučajevima nadopuniti kako bi bili preciznije određeni, primjerice rowValues
.
Sljedeći nazivi se trebaju upotrebljavati kada se pojavljuju isti koncepti. Time se osigurava konzistentnost i jasnoća kroz različite dijelove API-ja.
Naziv | Tip | Opis |
---|---|---|
name |
string |
Polje name treba sadržavati relativno ime resursa. |
parent |
string |
Za definicije resursa i zahtjeve za dohvaćanje/kreiranje, polje parent treba sadržavati relativno ime roditeljskog resursa. |
createTime |
Timestamp |
Vrijeme kreiranja entiteta. |
updateTime |
Timestamp |
Vrijeme posljednjeg ažuriranja entiteta. Napomena: update_time se ažurira kada se izvrši operacija kreiranja/ažuriranja/brisanja. |
deleteTime |
Timestamp |
Vrijeme brisanja entiteta, samo ako se entitet označava kao obrisan. |
expireTime |
Timestamp |
Vrijeme isteka entiteta ako isti istekne. |
startTime |
Timestamp |
Vrijeme početka određenog vremenskog razdoblja. |
endTime |
Timestamp |
Vrijeme završetka određenog vremenskog razdoblja ili operacije (bez obzira na njen uspjeh). |
readTime |
Timestamp |
Vrijeme u kojem se entitet treba pročitati (ako se koristi u zahtjevu) ili je pročitan (ako se koristi u odgovoru). |
timeZone |
string |
Naziv vremenske zone. Trebao bi biti IANA TZ naziv, kao što je "America/Los_Angeles". Za više informacija, pogledajte https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. |
regionCode |
string |
Unicode kod zemlje/regije (CLDR) lokacije, kao što su "US" i "419". Za više informacija, pogledajte http://www.unicode.org/reports/tr35/#unicode_region_subtag. |
languageCode |
string |
BCP-47 jezični kod, kao što su "en-US" ili "sr-Latn". Za više informacija, pogledajte http://www.unicode.org/reports/tr35/#Unicode_locale_identifier. |
mimeType |
string |
Objavljena MIME vrsta od strane IANA (također poznata kao media type). Za više informacija, pogledajte https://www.iana.org/assignments/media-types/media-types.xhtml. |
displayName |
string |
Prikazano ime entiteta. |
title |
string |
Službeno ime entiteta, kao što je naziv tvrtke. Treba se tretirati kao formalna verzija prikazanog imena. |
description |
string |
Jedan ili više paragrafa teksta s opisom entiteta. |
filter |
string |
Standardni parametar filtra za metode popisa. Pogledajte AIP-160. |
query |
string |
Isto kao i filter ako se primjenjuje na metodu pretraživanja (npr. :search) |
pageToken |
string |
Token za paginaciju u zahtjevu za popis. |
page |
int32 |
Stranica u paginaciji (query parametar). |
currentPage |
int32 |
Trenutna stranica u paginaciji (response body). |
pageSize |
int32 |
Veličina paginacije u zahtjevu za popis. |
totalPages |
int32 |
Ukupan broj stranica liste bez obzira na paginaciju. |
totalSize |
int32 |
Ukupan broj stavki u listi bez obzira na paginaciju. |
nextPageToken |
string |
Sljedeći token za paginaciju u odgovoru na popis. Trebao bi se koristiti kao pageToken za sljedeći zahtjev. Prazna vrijednost znači da više nema rezultata. |
orderBy |
string |
Navodi redoslijed rezultata za zahtjeve dohvata nad kolekcijom. |
progressPercent |
int32 |
Navodi napredak akcije u postocima (0-100). Vrijednost -1 znači da je napredak nepoznat. |
requestId |
string |
Jedinstveni ID string za otkrivanje dupliciranih zahtjeva. |
labels |
map<string, string> |
Predstavlja korisničke oznake resursa. |
showDeleted |
bool |
Ako resurs dopušta povratak izbrisanih entiteta, metoda dohvata kolekcije mora imati parametar show_deleted kako bi klijent mogao otkriti obrisane resurse. |
validateOnly |
bool |
Ako je true, označava da se zahtjev samo treba validirati, a ne izvršiti. |
Parametri i atributi API-ja koji služe kao izbornici opcija mogu biti definirani kao bool
, enum
ili string
.
Smjernice za odabir tipa:
- Vrijednosti
bool
su prikladne za binarne opcije za koje smo sigurni da se neće proširivati, npr. za parametre poputshowDeleted
. Može poprimiti vrijednostitrue
ilifalse
. - Vrijednosti
enum
su prikladne za opcije koje bi se moglo proširivati, npr.priority
ilistatus
. - Vrijednosti
string
su prikladne za opcije koje će se gotovo sigurno proširivati i to relativno često, npr.type
ilirole
, ili za opcije koje su definirane po nekom vanjskom standardu, npr.mimeType
ililanguageCode
.
Vrijednosti enum
trebaju biti definirane u dokumentaciji API-ja, i moraju imati TYPE_UNKNOWN
kao prvu opciju (npr. STATUS_UNKNOWN
). Time se omogućava dodavanje novih vrijednosti bez narušavanja kompatibilnosti s klijentima koji ih još nisu implementirali.
Svi ID-jevi moraju biti definirani kao stringovi zbog uniformnosti, fleksibilnosti i backwards compatibilityja. Time se nastoji izbjeći da zbog promjene tipa ID-ja dođe do problema s klijentima koji se koriste starim tipom ID-ja.
Datumi i vrijeme moraju pratiti ISO 8601 standard:
- Vrijeme se uvijek definira kao UTC timestamp u formatu
YYYY-MM-DDTHH:MM:SSZ
. Time se osigurava da se svi klijenti koriste istim vremenom i da se izbjegnu problemi s vremenskim zonama.- Primjer:
2024-01-01T00:00:00Z
. - Ako je potrebno, mogu se dodati i milisekunde:
2024-01-01T00:00:00.000Z
.
- Primjer:
- Datumi se uvijek definiraju u formatu
YYYY-MM-DD
.- Primjer:
2024-01-01
.
- Primjer:
API komunicira putem HTTP protokola. Svi zahtjevi i odgovori moraju biti u formatu JSON.
Struktura JSON odgovora mora pratiti sljedeću shemu:
{
"data": {
"collectionName": {
"attribute1": "value1",
"attribute2": "value2"
}
}
}
ili za kolekciju...
{
"data": {
"collectionName": [
{
"attribute1": "value1",
"attribute2": "value2"
},
{
"attribute1": "value1",
"attribute2": "value2"
}
]
}
}
ili za resurs s ugniježđenim resursima...
{
"data": {
"collectionName": {
"attribute1": "value1",
"attribute2": "value2",
"nestedCollection": [
{
"attribute1": "value1",
"attribute2": "value2"
},
{
"attribute1": "value1",
"attribute2": "value2"
}
]
}
}
}
U slučaju da array (niz) ne sadrži podatke, tada se vraća prazan array, a ne null
:
{
"data": {
"collectionName": []
}
}
API bi trebao imati osnovni URL koji se sastoji od verzije API-ja, npr. https://example.com/api/v1
.
API bi trebao imati statusni endpoint (/status
) koji vraća status i detalje API-ja:
- opći status (
status
) API-ja i resursa o kojima ovisi (poput baza podataka, drugih API-ja...):Healthy
/Unhealthy
- trenutnu verziju (
version
) API-ja:v1
/v2
- okruženje (
environment
):Production
/Staging
- vrijeme na serveru (
timestamp
):2024-01-01T00:00:00Z
Primjer odgovora statusnog endpointa:
{
"data": {
"status": "Healthy",
"version": "v1",
"environment": "Production",
"timestamp": "2024-01-01T00:00:00Z",
"dependencies": {
"database": "Healthy",
"otherApi": "Healthy"
}
}
}
URL ne smije završavati s /
:
- Ispravno:
/users
- Neispravno:
/users/
Jedinstveni resurs je definiran na sljedeći način: /collection/{id}
.
Resursi mogu biti ugniježđeni (/collection/{id}/collection
) samo ako je to prirodno i ako podresurs sigurno ne može pripadati nijednom drugom resursu.
- Primjer:
/students/1234/enrolled-courses
. - Ako se želi, primjerice, pod kolegijem studenta dohvatiti predavač, tada bi odgovor trebao sadržavati hyperlink na predavača:
{
"data": {
"enrolledCourses": [
{
"id": "1234",
"name": "Calculus",
"lecturer": {
"id": "5678",
"firstName": "John",
"lastName": "Doe",
"url": "/lecturers/5678"
}
}
]
}
}
⛔ Ovo se ne smije raditi jer profesori (lecturers
) mogu biti definirani i samostalno, tj. ne pripadaju samo i jedino upisanim kolegijima studenta:
GET /students/1234/enrolled-courses/1234/lecturers/5678
Upotrijebi /collection/-/collection
za sve ugniježđene resurse bez obzira na nadređene resurse, ako je ta funkcionalnost potrebna.
- Rezultat je iste strukture kao i za
/collection/{id}/collection
. - Primjer:
GET /students/-/enrolled-courses
dohvaća sve upise kolegije svih studenata.
Upotrijebi /collection/-/collection/{id}
za određeni ugniježđeni resurs bez obzira na nadređeni resurs, ako je ta funkcionalnost potrebna.
- Rezultat je iste strukture kao i za
/collection/{id}/collection/{id}
. - Primjer:
GET /students/-/enrolled-courses/1234
dohvaća upis kolegija s ID-jem 1234 bez obzira na studenta.
- Izbjegavati glagole u URL-ovima. Upotrijebiti HTTP metode za akcije nad resursima.
- Primjer:
POST /users
umjesto/users/create-user
- Primjer:
GET /users/{id}
umjesto/users/get-user
- Primjer:
PATCH /users/{id}
umjesto/users/update-user
- Primjer:
DELETE /users/{id}
umjesto/users/delete-user
- Primjer:
- Za akcije koje nisu CRUD operacije tj. one koje su kompleksnije, upotrijebiti HTTP metodu koja najbolje odgovara željenoj akciji (najčešće
POST
), imenovati akciju glagolom i dodati ju podactions
u URL-u resursa (/collection/{id}/actions/do-something
). Takve akcije treba izbjegavati ako je moguće.- Primjer: Za slanje emaila korisniku, upotrijebiti
POST /users/{id}/actions/send-email
. - Primjer: Za promjenu statusa studenta u alumni, upotrijebiti
POST /students/{id}/actions/graduate
. - Primjer: Za dohvat statusa studenta, upotrijebiti
GET /students/{id}
umjestoGET /students/{id}/actions/get-status
jer podaci studenta sigurno sadrže i njegov status.
- Primjer: Za slanje emaila korisniku, upotrijebiti
- Akcije koje nisu CRUD operacije se definiraju isključivo glagolima, a ako je neki glagol ujedno i imenica tada je potrebno upotrijebiti drugačiji izraz kako bi bilo jasno da se radi o akciji.
- Primjer:
POST /users/{id}/actions/send-email
umjestoPOST /users/{id}/actions/email
. - Primjer:
POST /stocks/{id}/actions/trade-stock
umjestoPOST /stocks/{id}/actions/trade
.
- Primjer:
- Ako se akcija ne smije ponoviti više puta, odnosno kada ista akcija ne rezultira uvijek istim rezultatom, tada je uz akciju potrebno poslati i
requestId
koji je jedinstveni string za otkrivanje dupliciranih zahtjeva. API bi trebao provjeriti je li akcija s istimrequestId
-jem već poslana i ako je, vratiti isti rezultat. Time se izbjegava dvostruka obrada iste akcije, i osigurava da će klijent dobiti rezultat ako ga je ranije propustio.
Akcija | Metoda | Request URL | Primjer |
---|---|---|---|
Dohvat jednog resursa po ID-ju | GET | /collection/{id} |
GET /users/1234 umjesto GET /users?id=1234 |
Dohvat više resursa s različitim ID-jevima | POST | /collection/actions/batch-fetch |
POST /users/actions/batch-fetch s request bodyjem {"ids": ["1", "2", "3"]} |
Dohvat svih resursa | GET | /collection |
GET /users |
Filtriranje | GET | /collection?attribute=value |
GET /users?name=John&age=20 |
Filtriranje (poseban filter) | GET | /collection/{filter} |
GET /academic-years/current za dohvat trenutne akademske godine |
Sortiranje | GET | /collection?orderBy=field:direction,field:direction |
GET /users?orderBy=name:asc,age:desc |
Paginacija (offset) | GET | /collection?pageSize=10&page=2 |
GET /users?pageSize=10&page=2 |
Paginacija (cursor) | GET | /collection?pageSize=10&pageToken=token |
GET /users?pageSize=10&pageToken=token |
Pretraga | GET | /collection/search?query=value |
GET /users/search?query=John |
Rad s resursom trenutnog korisnika (opcionalno) | GET | /collection/me |
GET /students/me/enrolled-courses |
Primjer uporabe kombinacije akcija:
GET /users?name=John&age=20&orderBy=name:asc,age:desc&pageSize=10&page=2
Ako primijenjeni filtri ili parametri paginacije ne daju rezultate, API tada mora vratiti praznu listu. Na primjer.:
{
"data": {
"users": []
}
"pagination": {
"currentPage": 1,
"pageSize": 0,
"totalPages": 0,
"totalSize": 0
}
}
Filtriranje se može upotrijebiti za dohvaćanje resursa koji zadovoljavaju određene kriterije:
- Kriteriji za filtriranje se šalju kao query parametri u formatu
field=value
. Na primjer,/users?name=John
. - Ako je potrebno filtrirati po rasponima, upotrebljavaju se
min
imax
prefiksi. Na primjer,/users?minAge=18&maxAge=30
. - Za uključivanje više vrijednosti, upotrebljava se zarez. Na primjer,
/users?role=admin,user
. - Za filtriranje pomoću boolean vrijednosti, upotrebljava se
true
ifalse
. Na primjer,/users?isActive=true
.
Sortiranje se može upotrebljavati za dohvaćanje resursa sortiranih prema određenom kriteriju:
- Za sortiranje se uvijek upotrebljava query parametar
orderBy
, a podaci za sortiranje se šalju u formatufield:direction
, gdje jefield
naziv atributa po kojem se sortira, adirection
smjer sortiranja (asc
za uzlazno idesc
za silazno). - Sintaksa za sortiranje je sljedeća:
/collection?orderBy=field1:direction,field2:direction
. Na primjer,/users?orderBy=name:asc,age:desc
za sortiranje po imenu uzlazno i dobi silazno.
Sortiranje uvijek treba biti stabilno. To znači da ako dva resursa imaju istu vrijednost po kojoj se sortira, mora biti jednoznačno određeno kojim redoslijedom se pojavljuju. To se postiže tako da se dodaju sekundarni kriteriji sortiranja. Na primjer, ako se sortira po imenu, a dva resursa imaju isto ime, tada se sortira i po ID-ju.
U svakoj dinamičnoj ili većoj kolekciji resursa mora biti omogućena paginacija. To isključuje kolekcije koje se gotovo nikad ne mijenjaju, poput statusa, tipova i vrsta. Razlog za to je da sve veće kolekcije moraju u jednom trenutku imati paginaciju kako bi se smanjila količina podataka koja se prenosi i kako bi se smanjilo opterećenje servera. A budući da je dodavanje paginacije breaking change, ona se mora dodati u početku.
Paginacija je breaking change zato što bez parametra količine rezultata klijent dobiva onoliko rezultata koliko server defaultno vraća, zbog čega klijent misli da je dobio sve rezultate kad zapravo nije.
Na API-ju mora biti postavljen defaultni broj rezultata po stranici, a klijent može postaviti željeni broj rezultata po stranici koji ne smije biti veći od maksimalnog broja rezultata definiranog na API-ju.
Kod paginacije se treba se primijeniti princip stabilnosti sortiranja kako bi se osigurao uvijek jednak redoslijed rezultata. Također je potrebno uz za svaku sljedeću stranicu slati primijenjene filtre i sortiranje.
Paginacija može biti ostvarena na dva osnovna načina:
Metoda | Opis | Primjer | Rezultat | Prednosti | Nedostaci |
---|---|---|---|---|---|
Offset-based paginacija | Klijent šalje broj rezultata po stranici i broj stranice koju želi dohvatiti. | GET /users?pageSize=10&page=2 |
- rezultati - trenutna stranica ( currentPage ) - broj rezultata na trenutnoj stranici ( pageSize ) - ukupan broj stranica ( totalPages ) - ukupan broj rezultata ( totalSize ) |
- jednostavno za implementaciju | - neefikasno za velike kolekcije - moguće preskakanje/dupliciranje rezultata ako dođe do promjene u podacima |
Cursor-based paginacija | Klijent šalje broj rezultata po stranici i token koji označava trenutnu poziciju u kolekciji (na prvom dohvatu podataka nema tokena). | GET /users?pageSize=10&pageToken=token |
- rezultati - sljedeći token ( nextPageToken ) ako ima još rezultata, inače null kao vrijednost tokena - broj rezultata na trenutnoj "stranici" ( pageSize ) |
- efikasno za velike kolekcije - nema preskakanja rezultata |
- složenija implementacija - prethodna stranica se pohranjuje na klijentu |
Page token je identifikator koji označava trenutnu poziciju u kolekciji. On može biti, primjerice, ID posljednjeg rezultata na trenutnoj stranici. Page token mora biti kriptiran i kodiran u base64
formatu kako bi se omogućila promjena implementacije paginacije bez utjecaja na klijente. Page token se šalje kao query parametar u zahtjevu za dohvat sljedeće stranice, a server vraća novi page token ako ima još rezultata.
Postoje još neki pristupi paginaciji poput bilježenja vremena inicijalnog dohvata te zatim dohvata snapshota podataka koji su stvoreni ili zadnji put mijenjani prije tog vremena, čime se može osigurati konzistentnost podataka. Međutim, takav pristup je složeniji i manje efikasan te nema mogućnost prikaza najnovijih podataka.
U svakom slučaju uz rezultate paginacije moraju se vratiti i metapodaci o paginaciji:
{
"data": {
"users": [
{
"id": "1",
"name": "John Doe"
},
...
]
},
"pagination": {
"currentPage": 1,
"pageSize": 10,
"totalPages": 5,
"totalSize": 50
}
}
ili...
{
"data": {
"users": [
{
"id": "1",
"name": "John Doe"
},
...
]
},
"pagination": {
"nextPageToken": "token",
"pageSize": 10
}
}
Za dohvat resursa (READ).
Mogući statusni kodovi i sadržaj odgovora:
Statusni kod | Opis | Response body | Response headers |
---|---|---|---|
200 OK |
Dohvat je uspješno izvršen. | Resurs | - |
202 Accepted |
Zahtjev je prihvaćen, ali nije završen. | Informacije za praćenje statusa obrade | - |
304 Not Modified |
Resurs nije promijenjen od zadnjeg upita (za uštedu bandwidtha). | - | - |
400 Bad Request |
Zahtjev nije ispravan. | Greška | - |
401 Unauthorized |
Korisnik nije autentificiran. | Greška | WWW-Authenticate s popisom načina autentikacije |
403 Forbidden |
Korisnik nema dozvolu za pristup resursu. | Greška | - |
404 Not Found |
Resurs nije pronađen (ili korisnik nema dozvolu). | Greška | - |
405 Method Not Allowed |
Metoda nije dozvoljena za traženi resurs. | Greška | Allow s popisom dozvoljenih metoda |
406 Not Acceptable |
Traženi format odgovora nije podržan (npr. zatražen XML umjesto JSON-a). | Greška | - |
429 Too Many Requests |
Premašen limit zahtjeva. | Greška | - |
500 Internal Server Error |
Greška na serveru. | Greška | - |
503 Service Unavailable |
Servis nije dostupan. | Greška | - |
504 Gateway Timeout |
Vrijeme odgovora je isteklo. | Greška | - |
NAPOMENE:
- Kada se dohvaća resurs koji zahtijeva autorizaciju, a korisnik nije autoriziran, mora se vratiti statusni kod
403 Forbidden
, osim u slučaju da bi samo odavanje informacije postoji li resurs moglo predstavljati kršenje privatnosti ili narušavanje sigurnosti, tada se vraća404 Not Found
.
Za kreiranje resursa (CREATE).
Mogući statusni kodovi i sadržaj odgovora:
Statusni kod | Opis | Response body | Response headers |
---|---|---|---|
200 OK |
Akcija je uspješno izvršena. | Resurs / Rezultat akcije | - |
201 Created |
Resurs je uspješno kreiran. | Resurs | Location s lokacijom novokreiranog resursa |
202 Accepted |
Zahtjev je prihvaćen, ali nije završen. | Informacije za praćenje statusa obrade | - |
204 No Content |
Akcija je uspješno izvršena (rezultat nije potrebno vratiti klijentu). | - | - |
400 Bad Request |
Zahtjev nije ispravan. | Greška | - |
401 Unauthorized |
Korisnik nije autentificiran. | Greška | WWW-Authenticate s popisom načina autentikacije |
403 Forbidden |
Korisnik nema dozvolu za pristup resursu. | Greška | - |
404 Not Found |
Resurs nije pronađen (ili korisnik nema dozvolu). | Greška | - |
405 Method Not Allowed |
Metoda nije dozvoljena za traženi resurs. | Greška | Allow s popisom dozvoljenih metoda |
406 Not Acceptable |
Traženi format odgovora nije podržan (npr. zatražen XML umjesto JSON-a). | Greška | - |
409 Conflict |
Resurs već postoji. | Greška | - |
415 Unsupported Media Type |
Tip medija nije podržan (primjerice klijent šalje XML umjesto JSON-a). | Greška | - |
429 Too Many Requests |
Premašen limit zahtjeva. | Greška | - |
500 Internal Server Error |
Greška na serveru. | Greška | - |
503 Service Unavailable |
Servis nije dostupan. | Greška | - |
504 Gateway Timeout |
Vrijeme odgovora je isteklo. | Greška | - |
507 Insufficient Storage |
Nedovoljno prostora za pohranu. | Greška | - |
NAPOMENE:
- Pri kreiranju resursa potrebno je poslati podatke u formatu JSON. Za podatke koje kreira server, kao što je ID resursa, u dokumentaciji treba jasno naznačiti da ih kreira server (npr. Output only).
Za ažuriranje resursa (UPDATE).
Mogući statusni kodovi i sadržaj odgovora:
Statusni kod | Opis | Response body | Response headers |
---|---|---|---|
200 OK |
Ažuriranje je uspješno izvršeno. | Resurs | - |
202 Accepted |
Zahtjev je prihvaćen, ali nije završen. | Informacije za praćenje statusa obrade | - |
400 Bad Request |
Zahtjev nije ispravan. | Greška | - |
401 Unauthorized |
Korisnik nije autentificiran. | Greška | WWW-Authenticate s popisom načina autentikacije |
403 Forbidden |
Korisnik nema dozvolu za pristup resursu. | Greška | - |
404 Not Found |
Resurs nije pronađen (ili korisnik nema dozvolu). | Greška | - |
405 Method Not Allowed |
Metoda nije dozvoljena za traženi resurs. | Greška | Allow s popisom dozvoljenih metoda |
406 Not Acceptable |
Traženi format odgovora nije podržan (npr. zatražen XML umjesto JSON-a). | Greška | - |
409 Conflict |
Resurs nije moguće ažurirati zbog konflikta. | Greška | - |
415 Unsupported Media Type |
Tip medija nije podržan (primjerice klijent šalje XML umjesto JSON-a). | Greška | - |
429 Too Many Requests |
Premašen limit zahtjeva. | Greška | - |
500 Internal Server Error |
Greška na serveru. | Greška | - |
503 Service Unavailable |
Servis nije dostupan. | Greška | - |
504 Gateway Timeout |
Vrijeme odgovora je isteklo. | Greška | - |
507 Insufficient Storage |
Nedovoljno prostora za pohranu. | Greška | - |
NAPOMENE:
- Metoda
PATCH
se upotrebljava za parcijalno ažuriranje resursa. Podaci za ažuriranje moraju biti poslani u formatu JSON u request bodyju. - Svi podaci koji su navedeni se ažuriraju, a svi ostali ostaju nepromijenjeni. Ako se neki atribut želi postaviti na
null
, tada se mora poslatinull
kao vrijednost. - Ako se uz ažuriranje želi napraviti i nešto drugo, to je moguće napraviti u istom requestu kroz query parametre. Na primjer,
PATCH /students/1234?notify=true
. - Ako se želi ažurirati resurs na način da se dogode još neke promjene u sustavu, tada se treba napraviti posebna akcija pod
actions
u URL-u umjesto da se radi običan update. Na primjer,POST /students/{id}/actions/graduate
.
Za zamjenu resursa (REPLACE).
VAŽNO: Metodu PUT
treba koristiti samo kada je potrebno zamijeniti cijeli resurs. Ako je potrebno ažurirati samo dio resursa, tada se treba koristiti PATCH
.
Mogući statusni kodovi i sadržaj odgovora:
Statusni kod | Opis | Response body | Response headers |
---|---|---|---|
200 OK |
Ažuriranje je uspješno izvršeno. | Resurs | - |
202 Accepted |
Zahtjev je prihvaćen, ali nije završen. | Informacije za praćenje statusa obrade | - |
400 Bad Request |
Zahtjev nije ispravan. | Greška | - |
401 Unauthorized |
Korisnik nije autentificiran. | Greška | WWW-Authenticate s popisom načina autentikacije |
403 Forbidden |
Korisnik nema dozvolu za pristup resursu. | Greška | - |
404 Not Found |
Resurs nije pronađen (ili korisnik nema dozvolu). | Greška | - |
405 Method Not Allowed |
Metoda nije dozvoljena za traženi resurs. | Greška | Allow s popisom dozvoljenih metoda |
406 Not Acceptable |
Traženi format odgovora nije podržan (npr. zatražen XML umjesto JSON-a). | Greška | - |
409 Conflict |
Resurs nije moguće ažurirati zbog konflikta. | Greška | - |
415 Unsupported Media Type |
Tip medija nije podržan (primjerice klijent šalje XML umjesto JSON-a). | Greška | - |
429 Too Many Requests |
Premašen limit zahtjeva. | Greška | - |
500 Internal Server Error |
Greška na serveru. | Greška | - |
503 Service Unavailable |
Servis nije dostupan. | Greška | - |
504 Gateway Timeout |
Vrijeme odgovora je isteklo. | Greška | - |
507 Insufficient Storage |
Nedovoljno prostora za pohranu. | Greška | - |
NAPOMENE:
- Ovu metodu treba izbjegavati zbog backwards compatibilityja, jer ako se dodaju novi atributi u resurs, a klijent ne zna za njih, tada će ih zamijeniti s
null
. - Metoda
PUT
se upotrebljava za zamjenu resursa. Podaci za zamjenu moraju biti poslani u formatu JSON u request bodyju. - Ako se uz zamjenu resursa želi napraviti i nešto drugo, to je moguće napraviti u istom requestu kroz query parametre. Na primjer,
PUT /students/1234?notify=true
. - Svi podaci koji su navedeni se zamjenjuju, a svi ostali se brišu. Ako se neki atribut želi postaviti na
null
, tada se mora poslatinull
kao vrijednost, ili ga se može izostaviti pa će biti prebrisan (postavljen nanull
).
Za brisanje resursa (DELETE).
Mogući statusni kodovi i sadržaj odgovora:
Statusni kod | Opis | Response body | Response headers |
---|---|---|---|
202 Accepted |
Zahtjev je prihvaćen, ali nije završen. | Informacije za praćenje statusa obrade | - |
204 No Content |
Resurs je uspješno obrisan. | - | - |
400 Bad Request |
Zahtjev nije ispravan. | Greška | - |
401 Unauthorized |
Korisnik nije autentificiran. | Greška | WWW-Authenticate s popisom načina autentikacije |
403 Forbidden |
Korisnik nema dozvolu za pristup resursu. | Greška | - |
404 Not Found |
Resurs nije pronađen (ili korisnik nema dozvolu). | Greška | - |
405 Method Not Allowed |
Metoda nije dozvoljena za traženi resurs. | Greška | Allow s popisom dozvoljenih metoda |
409 Conflict |
Resurs nije moguće izbrisati zbog konflikta. | Greška | - |
429 Too Many Requests |
Premašen limit zahtjeva. | Greška | - |
500 Internal Server Error |
Greška na serveru. | Greška | - |
503 Service Unavailable |
Servis nije dostupan. | Greška | - |
504 Gateway Timeout |
Vrijeme odgovora je isteklo. | Greška | - |
NAPOMENE:
- Metoda
DELETE
se upotrebljava za brisanje resursa. - Metoda
DELETE
nema request body. - Ako metoda
DELETE
odmah izbriše resurs, tada mora vratiti statusni kod204 No Content
bez response bodyja. - Ako je brisanje resursa duže od normalnog vremena odgovora, tada se vraća statusni kod
202 Accepted
zajedno s long-running operation. - Ako brisanje resursa nije pravo brisanje nego promjena statusa ili zastavice, tada se vraća statusni kod
200 OK
i promijenjeni resurs. - Kada se koristi HTTP metoda
DELETE
za brisanje resursa koji ne postoji, mora se vratiti statusni kod404 Not Found
.
Za dohvat informacija o dozvoljenim metodama za resurs.
Mogući statusni kodovi i sadržaj odgovora:
Statusni kod | Opis | Response body | Response headers |
---|---|---|---|
200 OK |
Dohvat je uspješno izvršen. | Informacije o mogućim query parametrima | Allow s popisom dozvoljenih metoda |
401 Unauthorized |
Korisnik nije autentificiran. | Greška | WWW-Authenticate s popisom načina autentikacije |
403 Forbidden |
Korisnik nema dozvolu za pristup resursu. | Greška | - |
404 Not Found |
Resurs nije pronađen. | Greška | - |
500 Internal Server Error |
Greška na serveru. | Greška | - |
503 Service Unavailable |
Servis nije dostupan. | Greška | - |
504 Gateway Timeout |
Vrijeme odgovora je isteklo. | Greška | - |
NAPOMENE:
- Metoda
OPTIONS
se upotrebljava za dohvat informacija o dozvoljenim metodama za resurs. - U response headers se šalje
Allow
s popisom dozvoljenih metoda za traženi resurs. - Ovu metodu nije potrebno implementirati, ali omogućava klijentima da saznaju koje metode su dozvoljene za traženi resurs.
Za dohvat informacija o resursu bez samog resursa.
Mogući statusni kodovi i sadržaj odgovora:
Statusni kod | Opis | Response body | Response headers |
---|---|---|---|
200 OK |
Dohvat je uspješno izvršen. | - | - |
401 Unauthorized |
Korisnik nije autentificiran. | Greška | WWW-Authenticate s popisom načina autentikacije |
403 Forbidden |
Korisnik nema dozvolu za pristup resursu. | Greška | - |
404 Not Found |
Resurs nije pronađen. | Greška | - |
500 Internal Server Error |
Greška na serveru. | Greška | - |
503 Service Unavailable |
Servis nije dostupan. | Greška | - |
504 Gateway Timeout |
Vrijeme odgovora je isteklo. | Greška | - |
NAPOMENE:
- Metoda
HEAD
se upotrebljava za dohvat informacija o resursu bez samog resursa. - Ova metoda se koristi za dohvat informacija o resursu, kao što su veličina resursa, tip medija, datum zadnje promjene i sl.
- Ovu metodu nije potrebno implementirati, ali omogućava klijentima da saznaju informacije o resursu bez potrebe za dohvatom cijelog resursa.
- Autentikacija predstavlja proces provjere identiteta korisnika.
- Kada se korisnik autentificira, server mora vratiti access token kao JWT, i refresh token.
- Access token se upotrebljava za autorizaciju korisnika, a refresh token za dobivanje novog access tokena.
- Autorizacija je proces određivanja prava pristupa resursima.
- Ako resurs nije javno dostupan, a korisnik ne pošalje valjani access token, server mora vratiti statusni kod
401 Unauthorized
. - Ako korisnik nema odgovarajuću ulogu za traženu akciju, server mora vratiti statusni kod
403 Forbidden
, osim u slučaju da bi samo odavanje informacije postoji li resurs moglo predstavljati kršenje privatnosti ili narušavanje sigurnosti. Tada se mora vratiti statusni kod404 Not Found
.
Kada korisnik pokuša pristupiti resursu koji zahtijeva autorizaciju, mora poslati access token.
Primjer zahtjeva:
GET /users
Authorization
Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJ1c2VyX25hbWUiOiJKb2huIERvZSIsInVzZXJfZW1haWwiOiJqb2huQGVtYWlsLmNvbSJ9.3JVAJ9Jf
Svi zahtjevi i statusni kodovi odgovora mogu biti logirani.
Logovi mogu sadržavati sljedeće informacije:
- vrijeme zahtjeva
- URL zahtjeva
- HTTP metoda zahtjeva
- query parametri zahtjeva
- vrijeme odgovora
- statusni kod odgovora
- identifikator korisnika koji je poslao zahtjev (ako je korisnik autentificiran)
- IP adresa klijenta koji je poslao zahtjev
- user-agent klijenta koji je poslao zahtjev (npr. browser ili aplikacija, verzija i sl.)
- poruka greške (ako je došlo do greške)
- vrijeme izvođenja zahtjeva na bazi podataka (ako je potrebno)
Server mora vraćati odgovarajuće statusne kodove i poruke grešaka.
Statusni kodovi i poruke grešaka moraju biti informativni i korisni.
Može se dogoditi da klijent pošalje zahtjev koji nije sasvim neispravan, ali nije u potpunosti ispravan. Na primjer, kod paginacije klijent može poslati zahtjev za dohvat stranice koja ne postoji. Tada treba vratiti statusni kod 200 OK
uz prazan skup podataka (vidi upute za dohvat resursa). Ili kada klijent zatraži više rezultata po stranici nego što je dozvoljeno. Tada treba uzeti u obzir da klijent ne zna koliko rezultata po stranici je dozvoljeno, pa je potrebno izvršiti paginaciju s najvećim dozvoljenim brojem rezultata po stranici.
Ako se pak radi o djelomičnom ažuriranju resursa (metoda PATCH
) i klijent pošalje podatke koji ne postoje, tada bi bilo dobro ažurirati samo one podatke koji postoje, a ostale ignorirati. Razlog tome je što se može dogoditi da su neki atributi uklonjeni, ali klijent ne zna za tu promjenu.
Greške moraju sadržavati informacije o uzroku greške i o načinu rješavanja greške.
Kada pišeš poruku greške prati sljedeće smjernice:
- Nemoj pretpostavljati da je korisnik upoznat s API-jem. Korisnik može biti developer klijentske aplikacije, administrativni korisnik, IT podrška ili običan korisnik aplikacije.
- Nemoj pretpostavljati da korisnik zna išta o usluzi ili da je upoznat s terminologijom.
- Kada god je to moguće, poruke greške trebaju biti napisane na način da tehnički stručan korisnik može ispraviti problem.
- Poruka greške mora biti kratka.
Obavezni atributi greške su code
i message
, ostali atributi su opcionalni:
target
koji označava na koji dio zahtjeva se greška odnosidetails
koji sadrži dodatne informacije o grešci
Struktura greške je sljedeća:
{
"error": {
"code": "badRequest",
"message": "Multiple errors in ContactInfo data",
"target": "contactInfo",
"details": [
{
"code": "nullValue",
"target": "phoneNumber",
"message": "Phone number must not be null"
},
{
"code": "nullValue",
"target": "lastName",
"message": "Last name must not be null"
},
{
"code": "malformedValue",
"target": "address",
"message": "Address is not valid"
}
]
}
}
Caching se može upotrijebiti za pohranu odgovora na serveru ili na klijentu, u svrhu smanjenja opterećenja servera i ubrzanja dohvata podataka.
Server upravlja svojim cacheom i daje upute klijentu kako da upravlja svojim cacheom. To može činiti na sljedeće načine:
- Upotrebom HTTP zaglavlja
Cache-Control
za upravljanje cacheom na klijentu. Slijedi opis mogućih vrijednostiCache-Control
zaglavlja:public
- odgovor se može pohraniti u cache i dijeliti s drugim korisnicimaprivate
- odgovor se može pohraniti u cache samo na klijentuno-cache
- odgovor se ne može pohraniti u cacheno-store
- odgovor se ne smije pohraniti u cachemax-age
- maksimalno vrijeme u sekundama na koje se odgovor može pohraniti u caches-maxage
- maksimalno vrijeme u sekundama na koje se odgovor može pohraniti u cache na shared cacheustale-while-revalidate
- vrijeme u sekundama na koje se odgovor može koristiti iz cachea nakon istekamax-age
dok se odgovor revalidirastale-if-error
- vrijeme u sekundama na koje se odgovor može koristiti iz cachea ako je došlo do greške prilikom revalidacijemust-revalidate
- klijent mora revalidirati odgovor s serverom prije dohvata iz cacheaproxy-revalidate
- proxy mora revalidirati odgovor s serverom prije dohvata iz cacheaimmutable
- odgovor se neće mijenjati tijekom vremenano-transform
- proxy ne smije mijenjati odgovoronly-if-cached
- klijent traži odgovor samo iz cachea
- Upotrebom HTTP zaglavlja
ETag
za provjeru promjena na resursu:- Klijent šalje inicijalni zahtjev za resursom.
- Server vraća resurs s
ETag
zaglavljem. - Klijent pohranjuje odgovor i njegov
ETag
. - Klijent šalje novi zahtjev za resursom s
ETag
zaglavljem. - Ako se resurs nije promijenio, server vraća statusni kod
304 Not Modified
.
200 OK
- dohvat je uspješno izvršen201 Created
- resurs je uspješno kreiran202 Accepted
- zahtjev je prihvaćen, ali nije završen204 No Content
- resurs je uspješno obrisan304 Not Modified
- resurs nije promijenjen (za uštedu bandwidtha)400 Bad Request
- zahtjev nije ispravan401 Unauthorized
- korisnik nije autentificiran403 Forbidden
- korisnik nema dozvolu za pristup resursu404 Not Found
- resurs nije pronađen (ili korisnik nema dozvolu)405 Method Not Allowed
- metoda nije podržana406 Not Acceptable
- traženi format odgovora nije podržan (npr. zatražen XML umjesto JSON-a)409 Conflict
- resurs već postoji415 Unsupported Media Type
- tip medija nije podržan (primjerice XML umjesto JSON-a)429 Too Many Requests
- premašen limit zahtjeva500 Internal Server Error
- greška na serveru503 Service Unavailable
- servis nije dostupan504 Gateway Timeout
- vrijeme odgovora je isteklo507 Insufficient Storage
- nedovoljno prostora za pohranu
Preporučuje se uporaba Swaggera, tj. OpenAPI specifikacije za dokumentaciju API-ja. Time se osigurava da dokumentacija bude usklađena s implementacijom API-ja.
Dokumentacija mora sadržavati sljedeće informacije:
- glavni URL API-ja
- verziju API-ja
- način autentikacije
- popis resursa/akcija:
- URL resursa
- opis resursa/akcije
- parametre resursa/akcije i njihov opis, tip, informaciju jesu li obavezni ili ne, i defaultne vrijednosti
- sheme zahtjeva i odgovora
- primjere zahtjeva i odgovora
- moguće greške i njihov opis (uz statusne kodove)
- primjere grešaka
- kontaktne podatke za pitanja i povratne informacije
Treba biti jasno naznačeno tko kreira ID resursa, klijentska aplikacija ili server. Na primjer, naziv datoteke kreira aplikacija, a ID kreira server. Također, ako su neki atributi output only (poput ID-ja), to također treba biti jasno naznačeno.
- atribut - svojstvo resursa
- collection - skup resursa
- CRUD - Create, Read, Update, Delete
- HTTP metode - metode koje se koriste za komunikaciju između klijenta i servera
- JWT - JSON Web Token (token za autentikaciju)
- message field - svaki podatak koji se šalje u requestu ili responseu kroz path, query parametre ili request i response body
- parametar - dodatni podatak koji se šalje u URL-u
- resurs - entitet koji se može dohvatiti, kreirati, ažurirati ili brisati
- URL - Uniform Resource Locator (adresa resursa)
API bi trebao imati glavni URL koji se sastoji od verzije API-ja. Na primjer, https://example.com/api/v1
.
Ako bi promjena na API-ju mogla narušiti kompatibilnost s klijentima, tada se mora napraviti nova verzija API-ja. Inače je moguće samo nadograditi postojeći API.
Non-breaking promjene uključuju:
- dodavanje novih resursa (endpointova)
- dodavanje novih atributa u response bodyju
- dodavanje novih metoda nad resursima
- dodavanje novih akcija
- dodavanje novih opcionalnih request parametara koji ne mijenjaju logiku obrade zahtjeva ako nisu poslani
- dodavanje novih statusnih kodova
- dodavanje novih enum vrijednosti
- dodavanje novih poruka grešaka
- izmjena postojećih poruka grešaka
- izmjene koje ne mijenjaju logiku obrade zahtjeva
Ukratko, drži se sljedećih pravila:
- nemoj ništa uklanjati
- nemoj mijenjati nazive
- nemoj mijenjati logiku obrade zahtjeva
- nemoj mijenjati hijerarhijsku strukturu JSON-a odgovora
- opcionalni parametri ne smiju postati obavezni
- što god dodaješ mora biti opcionalno
Ako je operacija koja se izvršava na serveru duža od normalnog vremena odgovora, tada se mora vratiti statusni kod 202 Accepted
zajedno s long-running operation.
Long-running operation je resurs koji sadrži informacije o statusu operacije, kao što su status
, progress
, result
i sl.
Primjer long-running akcije:
{
"status": "running",
"progress": 50,
"result": null
}
Kada se API mijenja, a neke metode ili resursi postaju zastarjeli, tada se mora obavijestiti korisnike o depriciranim resursima i metodama.
To se može napraviti slanjem Deprecation
HTTP zaglavlja u odgovoru zajedno s UNIX timestampom od kada je resurs ili metoda zastarjela. Uz Deprecation
zaglavlje može se poslati i Sunset
zaglavlje koje sadrži ljudski čitljiv datum kada će resurs ili metoda biti uklonjeni, te Link
zaglavlje s poveznicom na dokumentaciju o promjenama.
Primjer HTTP zaglavlja:
HTTP/1.1 200 OK
Content-Type: application/json
Deprecation: @1688169599
Sunset: Fri, 31 Dec 2030 23:59:59 GMT
Link: <https://example.com/docs/v2>; rel="deprecation" type:"text/html"
- linkovi na druge resurse: https://web.archive.org/web/20171202064047id_/http://www.si-journal.org/index.php/JSI/article/viewFile/290/298
- long-running operations:
- greške u zahtjevima
- rate-limiting
- upotpuniti autentikaciju i autorizaciju
- file upload/download