REST API vodič: Kako napraviti pouzdan i jednostavan API
Ako pravite web aplikaciju, mobilnu aplikaciju ili bilo koji sistem u kome više različitih servisa treba međusobno da razmenjuje podatke, danas je već standard da za te potrebe koristite neki API.
Kao što već znate, API omogućava da jedna aplikacija pošalje zahtev drugoj aplikaciji, dobije podatke, sačuva izmene ili pokrene neku operaciju.
Na prvi pogled pravljenje REST API-ja može da deluje jednostavno. Potrebno je da napravite nekoliko ruta, povežete ih sa bazom i vratite podatke u JSON formatu.
Međutim, kada se broj funkcionalnosti poveća, kada više aplikacija počne da koristi isti API i kada promene u jednom delu sistema počnu da utiču na druge delove, tada možete da naiđete na određene probleme u radu aplikacije.
Zbog toga dobar REST API treba da bude jasan, dosledan, bezbedan, jednostavan za korišćenje i dovoljno stabilan da kasnije možete da ga menjate bez rušenja postojećih aplikacija.
Zato ćemo u ovom tekstu objasniti najvažnije prakse koje treba da imate u vidu kada projektujete REST API.
Koristite jasne i dosledne URL adrese
Jedna od prvih stvari koju ćete primetiti kada koristite neki API jesu njegove URL adrese. Ako su one jasno organizovane, mnogo ćete lakše razumeti kako taj API radi.
Kod REST API-ja URL uglavnom upućuje na neki resurs. Resurs može biti korisnik, proizvod, porudžbina, članak, račun ili bilo koji drugi podatak kojim sistem upravlja.
Zbog toga je bolje da URL sadrži imenicu, a ne neku konkretnu radnju.
Na primer:
GET /users
POST /orders
DELETE /products/123
Ovakve adrese jasno govore sa kojim resursom radite. Sama HTTP metoda govori šta želite da uradite.
Lošiji primer URL-a bi bio:
GET /getUsers
POST /createOrder
DELETE /deleteProduct/123
U slučaju ovog lošijeg primera radnja se ponavlja i u URL adresi i u HTTP metodi. To nije baš preporučljivo, jer vremenom može dovesti do neujednačenih i teško predvidivih endpointa.
Takođe, dobra praksa je da koristite množinu za kolekcije resursa i zadržite isti obrazac kroz ceo API.
Na primer:
GET /users
GET /users/123
POST /users
PATCH /users/123
DELETE /users/123
Kada vidite ovakav raspored, možete lako da pretpostavite kako će izgledati i endpointi za proizvode, porudžbine ili druge resurse.
Nemojte preterivati sa ugnježdenim rutama
Ponekad je korisno da URL prikaže vezu između dva resursa.
Na primer:
GET /users/123/orders
Ovaj endpoint jasno govori da želite da dobijete porudžbine određenog korisnika.
Slično tome:
GET /orders/456/items
može da vrati stavke određene porudžbine.
Problem nastaje kada URL postane previše dubok.
Na primer:
GET /users/123/orders/456/items/789/comments
Takve rute su teže za razumevanje, komplikovanije za održavanje i često upućuju na to da struktura resursa nije baš najbolje osmišljena.
U većini slučajeva dovoljno je najviše jedan ili dva nivoa ugnježdenosti. Ako morate da idete dublje, naša preporuka je da napravite poseban endpoint.
Umesto veoma duboke rute, možete imati:
GET /order-items/789/comments
Na taj način URL ostaje kraći i jednostavniji.
Koristite HTTP metode prema njihovoj nameni
HTTP metode već imaju jasno značenje. Ako ih koristite pravilno, API će biti predvidiv i lakši za korišćenje.
Kao što znate, najčešće se koriste metode GET, POST, PUT, PATCH i DELETE.
GET za preuzimanje podataka
GET treba da koristite kada želite da pročitate podatke, bez menjanja stanja na serveru.
Na primer:
GET /products
GET /products/123
GET /products?category=telefoni
GET zahtev ne bi trebalo da briše, dodaje ili menja podatke.
Zbog toga nije dobra praksa da imate endpoint kao što je:
GET /users/123/delete
Iako ovakav endpoint tehnički može da radi, on krši očekivano ponašanje i konvenciju vezano za GET. Pored toga, browseri, keš sistemi i drugi alati mogu automatski da ponove GET zahtev, što može dovesti do neželjenih posledica.
POST za kreiranje novih resursa
POST se najčešće koristi za kreiranje novih podataka.
Na primer:
POST /users
Uz zahtev šaljete podatke novog korisnika:
{
"name": "Marko Petrović",
"email": "marko@nekidomen.rs"
}
Server zatim kreira korisnika i vraća odgovor sa njegovim identifikatorom.
Za uspešno kreiranje resursa obično se koristi statusni kod 201 Created.
PUT za potpunu zamenu resursa
PUT koristite kada želite da zamenite ili ažurirate ceo resurs.
Na primer:
PUT /users/123
Uz PUT se najčešće šalju sva polja resursa, čak i kada menjate samo jedan deo.
{
"name": "Marko Petrović",
"email": "novi-email@nekidomen.rs",
"role": "admin"
}
Važna osobina PUT metode je da treba da bude idempotentna. To znači da ponavljanje potpuno istog zahteva ne treba da napravi dodatne izmene ili nove resurse.
PATCH za delimične izmene
PATCH je praktičniji kada želite da promenite samo jedno ili nekoliko polja.
Na primer:
PATCH /users/123
{
"email": "novi-email@nekidomen.rs"
}
U ovom slučaju nema potrebe da šaljete ime, ulogu i ostale podatke koji se ne menjaju.
Imajte u vidu da je PATCH posebno koristan kod velikih objekata sa mnogo polja.
DELETE za brisanje resursa
DELETE koristite za uklanjanje resursa.
Na primer:
DELETE /users/123
Posle uspešnog brisanja server može da vrati 204 No Content, bez tela odgovora, ili 200 OK sa informacijom o obrisanom resursu.
Vraćajte odgovarajuće HTTP statusne kodove
Statusni kodovi omogućavaju klijentskoj aplikaciji da odmah razume rezultat zahteva. Ako vaš API uvek vraća 200 OK, čak i kada zahtev nije uspeo, aplikacija mora dodatno da proverava telo odgovora i pokušava da zaključi šta se dogodilo.
To nepotrebno komplikuje frontend, mobilnu aplikaciju ili drugi servis koji koristi API.
Za uspešne zahteve najčešće se koriste:
- 200 OK kada je zahtev uspešno izvršen i odgovor sadrži podatke
- 201 Created kada je kreiran novi resurs
- 204 No Content kada je zahtev uspešan, ali nema potrebe za telom odgovora
- 202 Accepted kada je zahtev prihvaćen, ali obrada još nije završena
Za greške na strani klijenta koriste se kodovi iz grupe 4xx.
Na primer:
- 400 Bad Request kada zahtev nije pravilno formatiran
- 401 Unauthorized kada korisnik nije prijavljen ili token nije ispravan
- 403 Forbidden kada je korisnik prijavljen, ali nema dozvolu
- 404 Not Found kada resurs ne postoji
- 409 Conflict kada se zahtev sukobljava sa trenutnim stanjem
- 422 Unprocessable Entity kada su podaci pravilno poslati, ali nisu validni
Kod problema na serveru koriste se kodovi iz grupe 5xx.
Na primer:
- 500 Internal Server Error
- 502 Bad Gateway
- 503 Service Unavailable
- 504 Gateway Timeout
Dosledna upotreba statusnih kodova čini API mnogo jednostavnijim za integraciju.
Standardizujte odgovore sa greškama
Statusni kod govori koja vrsta problema se dogodila, ali često nije dovoljan da razumete šta tačno treba da ispravite.
Zbog toga je dobro da svi odgovori sa greškama imaju isti oblik.
Na primer:
{
"error": {
"code": "INVALID_EMAIL",
"message": "E-mail adresa nije ispravna.",
"field": "email"
}
}
Polje code treba da bude stabilno i namenjeno aplikaciji, dok message može da bude poruka koja se prikazuje krajnjem korisniku ili developeru.
Kod validacije forme korisno je da vratite sve greške odjednom.
Na primer:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Podaci nisu ispravno uneti.",
"details": [
{
"field": "email",
"message": "E-mail adresa je obavezna."
},
{
"field": "password",
"message": "Lozinka mora da ima najmanje osam karaktera."
}
]
}
}
Ako API vraća samo prvu grešku, korisnik mora više puta da šalje formu kako bi ispravio sva polja. Mnogo je praktičnije da vratite sve greške u jednom odgovoru.
Podrazumeva se da odgovor ne sme da otkriva interne podatke, SQL upite, putanje na serveru ili kompletan sadržaj greške iz aplikacije. Takvi podaci mogu da budu korisni u logovima, ali ne treba da ih šaljete korisniku API-ja.
Planirajte verzionisanje API-ja
Potpuno je očekivano da se vaš API vremenom menja. Da dodajete nova polja, menjate pravila validacije, uvodite nove načine prijave, uklanjate neke stare funkcionalnosti i sl.
Problem kod toga je što postojeće aplikacije mogu da zavise od starog ponašanja, pa ako promenite strukturu odgovora bez upozorenja, mobilna aplikacija koja je već instalirana kod korisnika može da prestane da radi.
Zbog toga je dobro da verzionisanje API-ja planirate od početka.
Najjednostavniji način za to je da se verzija nalazi u URL adresi.
Na primer:
GET /api/v1/users
GET /api/v2/users
Ovakav pristup je vrlo jasan. Odmah možete da vidite koju verziju API-ja aplikacija koristi, a više verzija može neko vreme da radi paralelno.
Druga mogućnost je verzionisanje preko zaglavlja zahteva, ali je ono nešto manje očigledno i zahteva da klijent pravilno postavi dodatna zaglavlja.
Dodavanje novog opcionog polja uglavnom ne zahteva novu verziju, jer postojeći klijenti mogu jednostavno da ga ignorišu.
Nova verzija je potrebna kada pravite promenu koja može da pokvari postojeće klijente. To može biti uklanjanje polja, promena tipa podatka, promena strukture odgovora ili potpuno drugačiji način autentifikacije.
Kod velikih lista uvedite paginaciju
Ako vaš endpoint vraća nekoliko korisnika ili proizvoda, neće biti problem da ih vrati sve odjednom.
Međutim, ako tabela ima desetine ili stotine hiljada redova, vraćanje svih podataka u jednom odgovoru može ozbiljno da optereti bazu, server i klijentsku aplikaciju.
Zbog toga velike kolekcije treba da podelite na manje stranice.
Jedan od najjednostavnijih pristupa je paginacija pomoću broja stranice.
GET /products?page=3&per_page=20
Odgovor može da sadrži podatke o trenutnoj stranici i ukupnom broju rezultata.
{
"data": [],
"pagination": {
"current_page": 3,
"total_pages": 15,
"total_items": 287,
"per_page": 20
}
}
Drugi pristup koristi limit i offset.
GET /products?limit=20&offset=40
Ovaj način je jednostavan, ali može da napravi problem ako se podaci često dodaju ili brišu između dva zahteva.
Kod feedova, poruka, aktivnosti i drugih podataka koji se često menjaju bolji izbor može da bude paginacija pomoću kursora.
GET /products?limit=20&cursor=abc123
Server vraća kursor za sledeću stranicu.
{
"data": [],
"pagination": {
"next_cursor": "def456",
"has_more": true
}
}
Ovaj pristup je stabilniji kada se sadržaj često menja.
Omogućite filtriranje, sortiranje i pretragu
Klijentska aplikacija ne treba da preuzima sve podatke, a zatim da ih filtrira na uređaju korisnika.
Mnogo je bolje da kroz API omogućite da se već u zahtevu navede šta je potrebno.
Na primer:
GET /products?category=telefoni&price_min=200&price_max=800
Za sortiranje možete da koristite parametar kao što je:
GET /products?sort=price:asc
GET /users?sort=created_at:desc
Ako je potrebno više kriterijuma:
GET /products?sort=category:asc,price:desc
Za tekstualnu pretragu možete da koristite:
GET /products?search=bežične slušalice
Važno je da nazivi parametara budu jasni i dosledni kroz ceo API.
Ako na jednom endpointu koristite search, na drugom q, a na trećem keyword, API tada postaje teži za pamćenje i korišćenje.
Zaštitite API autentifikacijom i HTTPS vezom
Bezbednost treba da bude deo osnovnog dizajna API-ja od samog starta.
Prvo pravilo je da sav saobraćaj ide preko HTTPS veze.
Bez HTTPS-a podaci se prenose u nešifrovanom obliku. To znači da neko može da presretne lozinke, tokene, lične podatke ili druge poverljive informacije.
Za jednostavne server-to-server integracije možete koristiti API ključeve.
Na primer:
X-API-Key: api_key_value
Za aplikacije u kojima se korisnici prijavljuju češće se koriste bearer tokeni i OAuth 2.0.
Authorization: Bearer token_value
Pored autentifikacije, potrebno je da proveravate i autorizaciju.
Autentifikacija odgovara na pitanje ko je korisnik, dok autorizacija određuje šta taj korisnik sme da uradi.
Na primer, običan korisnik možda može da vidi samo svoje porudžbine, dok administrator može da vidi sve porudžbine u sistemu.
Ograničite broj zahteva
Javni API lako može da postane meta automatizovanih zahteva, pokušaja zloupotrebe ili jednostavno loše napisane aplikacije koja šalje previše poziva.
Zbog toga je dobra praksa da uvedete ograničenje broja zahteva, odnosno rate limiting.
Na primer, jednom korisniku možete dozvoliti najviše 1.000 zahteva u toku jednog sata.
API može da vrati zaglavlja sa informacijom o trenutnom ograničenju:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 1760000000
Kada korisnik prekorači zadato ograničenje, server treba da vrati:
429 Too Many Requests
Dobro je da navedete i kada korisnik može ponovo da pokuša.
Ograničenja ne moraju biti ista za sve endpointe. Pretraga može imati jedno ograničenje, prijava drugo, a kreiranje skupih izveštaja treće.
Vodite računa o idempotentnosti
Kod mrežnih zahteva uvek postoji mogućnost da se veza prekine ili da klijent ne dobije odgovor na vreme.
U tom slučaju aplikacija može da ponovi zahtev.
Ukoliko se radi o običnom GET zahtevu, to uglavnom nije problem. Međutim, kod plaćanja, porudžbina ili rezervacija ponovljeni POST zahtev može da napravi duplikat.
Na primer, korisnik klikne na plaćanje, server obradi transakciju, ali aplikacija ne dobije odgovor zbog prekida veze. Aplikacija zatim ponovi isti zahtev i naplata se izvrši još jednom.
Da biste to sprečili, možete koristiti idempotency ključeve.
POST /payments
Idempotency-Key: unique-payment-key
Server pamti ključ i rezultat zahteva. Ako isti zahtev stigne ponovo sa istim ključem, server neće napraviti novu uplatu, već će vratiti prethodni odgovor.
Ovo je posebno važno kod plaćanja, kreiranja porudžbina i drugih operacija koje ne smeju slučajno da se izvrše više puta.
Pravilno podesite CORS
CORS određuje kojim web sajtovima je dozvoljeno da iz browsera pristupe vašem API-ju.
Na primer:
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PATCH, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Kod privatnog API-ja najbolje je da navedete tačne domene koji smeju da mu pristupe.
Korišćenje zvezdice:
Access-Control-Allow-Origin: *
može biti prihvatljivo za potpuno javne podatke, ali nije dobar izbor za API koji koristi korisničke naloge, tokene ili poverljive informacije.
Važno je da razumete da CORS nije zamena za autentifikaciju. Čak i kada je CORS pravilno podešen, server i dalje mora da proverava identitet korisnika i njegove dozvole.
Koristite dosledan format podataka
JSON je danas najčešći format za REST API-je.
Zahtevi i odgovori treba jasno da navedu format podataka.
Content-Type: application/json
Accept: application/json
Pored samog formata, važna je i doslednost naziva polja.
Ako koristite snake_case, koristite ga svuda:
{
"first_name": "Marko",
"created_at": "2026-07-10T10:00:00Z"
}
Ako koristite camelCase, onda sva polja treba da prate isti obrazac:
{
"firstName": "Marko",
"createdAt": "2026-07-10T10:00:00Z"
}
Nije toliko važno koji stil ćete izabrati, ali je važno da isti stil koristite kroz ceo API.
Isto važi za datume, iznose, valute, identifikatore i vrednosti koje mogu biti prazne.
Na primer, unapred odlučite da li prazno polje vraćate kao null, prazan string ili ga uopšte ne vraćate.
Nemojte vraćati više podataka nego što je potrebno
Veliki odgovori usporavaju aplikaciju, povećavaju potrošnju saobraćaja i otežavaju obradu podataka.
Ako klijentu treba samo ime i adresa elektronske pošte, nema potrebe da vaš API vraća ceo profil korisnika sa svim podešavanjima, ulogama, istorijom i povezanim podacima.
Možete omogućiti izbor polja:
GET /users/123?fields=name,email
Ovo može biti posebno korisno kod mobilnih aplikacija i sporijih internet veza.
Takođe treba da izbegavate duboko ugnježdene JSON strukture.
Ako odgovor sadrži korisnika, njegovu organizaciju, podešavanja organizacije, članove organizacije i njihove profile, vrlo brzo postaje težak za korišćenje.
Ponekad je bolje da vratite osnovne podatke i identifikatore, a dodatne informacije učitate posebnim zahtevom.
Ne izlažite unutrašnju strukturu sistema
API treba da predstavlja poslovne pojmove, a ne način na koji ste aplikaciju tehnički napravili.
Loš endpoint bi mogao da izgleda ovako:
GET /user_table_v2/query?db_id=123
Ovakva ruta otkriva detalje baze i interne implementacije.
Bolje je da koristite:
GET /users/123
Na taj način kasnije možete da promenite bazu, nazive tabela ili kompletnu unutrašnju arhitekturu, a da javni API ostane isti.
Isto važi i za odgovore. Nema potrebe da vraćate interne identifikatore tabela, sistemske zastavice ili tehnička polja koja klijentu realno nisu potrebna.
Vodite evidenciju o zahtevima i greškama
Kada vaš API radi u produkciji, nije dovoljno samo da znate da se dogodila greška. Potrebno je da možete da pronađete tačan zahtev koji ju je izazvao.
Zbog toga treba da beležite najmanje:
- HTTP metodu
- endpoint
- statusni kod
- vreme izvršavanja
- identitet korisnika kada je poznat
- vreme nastanka zahteva
- podatke o grešci
Veoma je korisno da svaki zahtev dobije jedinstveni identifikator.
Na primer:
X-Request-ID: f47ac10b-58cc-4372-a567-0e02b2c3d479
Ako korisnik prijavi problem i pošalje ovaj identifikator, možete mnogo lakše da pronađete zahtev u logovima.
Pored logova, dobro je da pratite broj grešaka, prosečno vreme odgovora, najsporije endpointe i opterećenje servera.
API koji nema dovoljno podataka za praćenje problema može biti veoma težak za održavanje, čak i kada je kod kvalitetno napisan.
Vodite računa o kompatibilnosti sa starijim klijentima
Kada vaš API počne da koristi mobilna aplikacija, web sajt ili spoljni partner, svaka promena postaje rizičnija.
Nove podatke obično možete da dodate bez velikog problema, jer će ih postojeći klijenti jednostavno ignorisati.
Mnogo veći problem nastaje kada uklonite polje, promenite njegov naziv ili promenite tip podatka.
Na primer, ako je price ranije bio broj:
{
"price": 1200
}
a zatim postane string:
{
"price": "1200 EUR"
}
postojeća aplikacija može prestati da radi.
Kada morate da uklonite staru funkcionalnost, dobro je da uvedete period zastarevanja.
To podrazumeva da:
- unapred najavite promenu
- obezbedite uputstvo za prelazak
- neko vreme podržavate staru i novu verziju
- jasno navedete datum gašenja stare verzije
Tako developerima koji koriste vaš API dajete dovoljno vremena da prilagode svoje aplikacije.
Napišite jasnu dokumentaciju
Čak i dobro projektovan API može biti težak za korišćenje ako nema jasnu dokumentaciju.
Za svaki endpoint treba da navedete:
- HTTP metodu
- URL
- potrebna zaglavlja
- parametre
- format tela zahteva
- format odgovora
- moguće statusne kodove
- način autentifikacije
- primere grešaka
Najkorisnija dokumentacija sadrži primere koje developer može odmah da kopira i testira.
Na primer:
POST /api/users
Content-Type: application/json
Authorization: Bearer token
{
"name": "Marko Petrović",
"email": "marko@nekidomen.rs"
}
Odgovor:
{
"id": 123,
"name": "Marko Petrović",
"email": "marko@nekidomen.rs",
"created_at": "2026-07-10T10:30:00Z"
}
Dokumentaciju treba da ažurirate zajedno sa kodom.
Jedan od najčešćih problema jeste da endpoint radi drugačije od onoga što piše u dokumentaciji. Tada developer gubi vreme pokušavajući da otkrije da li je problem u njegovom zahtevu ili u samoj dokumentaciji.
Zbog toga je korisno da koristite alate koji mogu da generišu dokumentaciju iz stvarne API specifikacije i omoguće direktno testiranje zahteva.
Testirajte više od uspešnog scenarija
Nije dovoljno da proverite samo da li endpoint radi kada su svi podaci ispravni.
Potrebno je da testirate i situacije u kojima:
- nedostaje obavezno polje
- token nije ispravan
- korisnik nema dozvolu
- resurs ne postoji
- podatak već postoji
- zahtev se ponavlja
- prekoračeno je ograničenje broja zahteva
- baza ili spoljni servis nisu dostupni
Jedinični testovi proveravaju pojedinačne delove aplikacije.
Integracioni testovi proveravaju da li više delova pravilno radi zajedno, na primer API, baza i autentifikacija.
End-to-end testovi proveravaju kompletan tok, od zahteva do konačnog rezultata.
Kod ozbiljnijih sistema posebno treba da testirate plaćanja, kreiranje porudžbina, promenu statusa i druge operacije koje mogu imati finansijske ili poslovne posledice.
Najčešće greške kod REST API-ja
Jedna od najčešćih grešaka je nedoslednost.
Na primer: na jednom endpointu koristite množinu, na drugom jedninu. Jedan odgovor vraća podatke u polju data, drugi direktno vraća objekat. Na jednom mestu greška ima polje message, a na drugom error_message.
Svaka takva razlika pojedinačno može da deluje nevažno, ali kada API poraste, korišćenje postaje sve teže.
Druga česta greška je da poslovnu logiku previše vežete za strukturu baze. Kada se promeni tabela ili odnos između podataka, mora da se menja i javni API.
Problem može da nastane i kada unapred ne razmišljate o velikoj količini podataka. Endpoint koji dobro radi sa 100 proizvoda može postati neupotrebljiv kada ih bude 100.000.
Česta greška je i vraćanje previše detaljnih poruka o greškama. Stack trace i SQL poruke mogu da vam pomognu u lokalnom okruženju, ali ne treba da budu vidljive korisnicima u produkciji.
Na kraju, veliki problem može da predstavlja i dokumentacija koja nije usklađena sa stvarnim ponašanjem API-ja.
Zaključak
Kao što ste videli dobar REST API ne mora da bude preterano komplikovan. Najvažnije je da bude dosledan, predvidiv i jednostavan za korišćenje.
URL adrese treba jasno da predstavljaju resurse, HTTP metode treba da koristite prema njihovoj nameni, a statusni kodovi i odgovori sa greškama treba da budu isti kroz ceo sistem.
Pored toga, potrebno je da na vreme razmišljate o verzionisanju, bezbednosti, paginaciji, ograničenju broja zahteva, dokumentaciji i praćenju grešaka.
Mnogi problemi sa API-jima nastaju zato što su se tokom vremena endpointi, strukture odgovora, primeri i dokumentacija menjali bez jasnog pravila.
Ako od početka postavite jednostavne i dosledne standarde, kasnije ćete lakše dodavati nove funkcionalnosti, povezivati nove aplikacije i održavati postojeće integracije.
