Kai pradedi kurti API, pirmas instinktas dažniausiai būna – tiesiog padaryti, kad veiktų. Endpointai atsiranda vienas po kito, pavadinimai priklauso nuo nuotaikos tą dieną, o klaidų kodai grąžinami taip, kaip tuo momentu patogiau. Praėjus pusmečiui ar metams, tas pats API tampa košmaru – ne tik kitiems programuotojams, bet ir tau pačiam. Todėl API dizainas nėra kosmetika, tai yra pati architektūros šerdis, kuri nulemia, kaip lengvai (ar sunkiai) sistema plėsis, integruosis su kitomis paslaugomis ir išgyvens laiko testą.
Šiame straipsnyje pereisime per pagrindinius principus, kurie skiria API, su kuriuo dirbti yra malonu, nuo API, kurio dokumentaciją tenka skaityti tris kartus, kad supratum, kodėl grąžinamas 500 kodas vietoje 400.
Nuoseklumas – draugas, kurio nepastebi, kol jo nėra
Nuoseklumas API dizaine reiškia, kad panašūs dalykai atrodo panašiai visur. Jei viename endpointe naudoji snake_case, kitame camelCase, o trečiame maišai abu – tu jau sukūrei problemą, kurios sprendimui reikės dokumentacijos, komentarų kode ir galiausiai – susierzinusio komandos nario Slack žinutėje.
Praktiškai tai reiškia kelis dalykus:
- Pasirink vieną raktažodžių formatą (JSON API dažniausiai renkasi
camelCase, kai kalbame apie Python pagrindu veikiančius backend’us –snake_case) ir laikykis jo visur. - URL struktūra turi būti nuspėjama. Jei turi
/users/{id}/orders, tai/products/{id}/reviewsturėtų sekti tą pačią logiką, o ne staiga tapti/products/{id}/getReviews. - Datų formatai – visada ISO 8601. Be išimčių, be „šitas endpointas grąžina Unix timestamp, o tas – žmogui skaitomą datą”.
Nuoseklumas neatrodo svarbus, kai kuri API vienas. Jis tampa kritiškai svarbus, kai komandoje dirba penki žmonės ir kiekvienas turi savo požiūrį į „teisingą” struktūrą.
Resursai, ne veiksmai – REST filosofijos esmė
Daugelis klaidų API dizaine kyla iš to, kad žmonės bando modeliuoti veiksmus, o ne resursus. Vietoje /getUserData ar /createNewOrder, REST filosofija sako – galvok apie daiktavardžius, ne veiksmažodžius. HTTP metodai (GET, POST, PUT, PATCH, DELETE) jau savaime nusako veiksmą.
Taigi vietoj:
POST /createOrder GET /getOrderById?id=123 POST /deleteOrder?id=123
Turėtų būti:
POST /orders GET /orders/123 DELETE /orders/123
Skirtumas atrodo kosmetinis, bet praktikoje tai keičia visą API architektūrą. Kai resursai yra pagrindinis principas, natūraliai atsiranda hierarchija, nuspėjamumas ir galimybė lengvai pridėti naujų operacijų nesugriaunant senų. Beje, tai nereiškia, kad reikia aklai laikytis REST dogmos – kai kuriems veiksmams (pvz., „siųsti pranešimą” ar „aktyvuoti paskyrą”) tikrai natūraliau tinka veiksmo pavadinimas endpoint’e, ir tai visiškai priimtinas kompromisas, kol jis nuoseklus.
Versijavimas – kaip nesugriauti to, kas jau veikia
Anksčiau ar vėliau tavo API pasikeis. Klientas paprašys naujo lauko, verslo logika evoliucionuos, arba tiesiog supranti, kad pirminis dizainas buvo prastas. Klausimas ne „ar reikės keisti”, o „kaip pakeisti nesulaužant to, kas jau naudojama produkcijoje”.
Populiariausi versijavimo būdai:
- URL versijavimas –
/v1/users,/v2/users. Paprasčiausias, aiškiausias, lengvai suprantamas net naujam komandos narys per pirmą dieną darbe. - Header versijavimas – klientas nurodo versiją per
Acceptarba custom header. Švariau iš „REST puristo” perspektyvos, bet sunkiau debug’inti ir testuoti naršyklėje ranka. - Query parametras –
/users?version=2. Retai naudojamas, dažniausiai tik pereinamiesiems etapams.
Realybėje URL versijavimas laimėja dažniausiai vien dėl paprastumo – jį lengva matyti logs’uose, lengva testuoti su Postman ar curl, lengva paaiškinti naujam kolegai. Bet nepriklausomai nuo pasirinkto metodo, svarbiausia taisyklė – niekada nekeisti egzistuojančio kontrakto be naujos versijos. Jei laukas anksčiau buvo string, o tapo objektu – tai jau ne patobulinimas, tai lūžis.
Klaidų pranešimai – kur slepiasi tikras profesionalumas
Yra API, kurie grąžina {"error": "Something went wrong"} su HTTP kodu 200. Ir yra API, kurie grąžina aiškią, struktūruotą klaidą su tinkamu statuso kodu, kuri leidžia iškart supraisti, kas nutiko ir kodėl.
Geras klaidos atsakymas turėtų turėti:
- Teisingą HTTP statuso kodą (400 – kliento klaida, 401 – neautentifikuotas, 403 – neturi teisių, 404 – resursas nerastas, 422 – validacijos klaida, 500 – serverio klaida).
- Aiškų, žmogui skaitomą pranešimą.
- Machine-readable klaidos kodą, kurį klientas galėtų naudoti programiškai (pvz.,
"code": "INVALID_EMAIL_FORMAT"), nes lokalizuoti tekstiniai pranešimai keičiasi, o kodai – ne. - Kai reikia – papildomos informacijos apie tai, kuris laukas ar parametras sukėlė problemą.
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Email formatas neteisingas",
"field": "email"
}
}Tokia struktūra leidžia frontend’o komandai automatizuoti klaidų apdorojimą, o ne rašyti dešimtis if sąlygų, bandant atspėti, ką reiškia tekstinis pranešimas skirtingomis kalbomis.
Puslapiavimas ir filtravimas – kai duomenų daug
Endpointas, grąžinantis visus vartotojus vienu kartu, veikia gerai su testine duomenų baze, kurioje yra dvidešimt įrašų. Produkcijoje, kai tų įrašų milijonas, tas pats endpointas gali sugriauti visą serverį arba tiesiog niekada neatsakyti.
Dažniausiai naudojami puslapiavimo būdai:
- Offset-based –
?limit=20&offset=40. Paprasta, bet turi problemų su našumu didelėse duomenų bazėse ir su duomenų nuoseklumu, kai įrašai pridedami/ištrinami tarp užklausų. - Cursor-based –
?limit=20&cursor=eyJpZCI6MTIzfQ==. Sudėtingiau įgyvendinti, bet stabiliau veikia su nuolat kintančiais duomenimis, socialinių tinklų feed’ai naudoja beveik tik šį metodą.
Filtravimas ir rikiavimas taip pat turėtų būti nuspėjami: ?status=active&sort=-createdAt yra intuityvu – minusas prieš lauko pavadinimą reiškia mažėjančią tvarką. Svarbu dokumentuoti, kokie filtrai palaikomi, nes priešingu atveju vartotojai bandys spėti ir gaus tuščius rezultatus, net nesupratę, kad parametras tiesiog neegzistuoja.
Autentifikacija ir teisės – ne vieta eksperimentams
Saugumo klausimuose improvizacija kainuoja skaudžiai. Šiandien standartas didžiajai daliai API yra OAuth 2.0 arba paprastesniais atvejais – API key su rate limiting’u. Svarbu atskirti autentifikaciją (kas tu esi) nuo autorizacijos (ką tau leidžiama daryti) – tai skirtingi sluoksniai, ir jų maišymas viename middleware sluoksnyje dažnai baigiasi bug’ais, kur vartotojas gauna prieigą prie duomenų, kurių neturėtų matyti.
Keli praktiniai dalykai, kuriuos verta turėti nuo pirmos dienos:
- Rate limiting – be jo, vienas piktavalis klientas gali paguldyti visą infrastruktūrą.
- Token’ų galiojimo laikas – niekada nedaryti amžinai galiojančių access tokenų.
- HTTPS visur, be išimčių, net vidinėse aplinkose.
- Slaptų duomenų neatiduoti response’uose – jei modelis turi
passwordHashlauką, jis niekada neturėtų pasiekti API atsakymo, net atsitiktinai per serializacijos klaidą.
Dokumentacija – ne priedas, o dalis produkto
API be dokumentacijos yra tarsi restoranas be meniu – galbūt maistas geras, bet niekas nesupras, ką užsisakyti. OpenAPI (anksčiau žinomas kaip Swagger) specifikacija tapo de facto standartu, ir jos naudojimas leidžia automatiškai generuoti interaktyvią dokumentaciją, kliento bibliotekas keliomis kalbomis, ir net testavimo scenarijus.
Gera dokumentacija turėtų atsakyti į tris klausimus be papildomų klausimų komandoje: ką endpoint’as daro, kokie parametrai reikalingi ir pasirenkami, ir kaip atrodo sėkmingas bei nesėkmingas atsakymas. Pavyzdžiai su realiais duomenimis (ne string ir 0 visur) padeda žymiai greičiau nei ilgi paaiškinimai tekstu.
Verta paminėti ir tai, kad dokumentacija, kuri atsilieka nuo realaus kodo, yra blogiau nei jos nebuvimas – ji kuria klaidingą pasitikėjimą. Todėl automatinis dokumentacijos generavimas iš kodo anotacijų (kaip tai daro FastAPI ar NestJS su Swagger integracija) yra žymiai patikimesnis sprendimas nei atskirai palaikomas Word failas, kurį atnaujina „kai turės laiko”.
Kai visi principai susijungia į vieną vientisą sistemą
Jei sudėti visus šiuos principus į vieną sakinį, gautųsi kažkas panašaus: gerai suprojektuotas API yra toks, kurio elgesį galima nuspėti nepaskaičius dokumentacijos kiekvieną kartą, kurio klaidos padeda, o ne trikdo, ir kuris keičiasi neardamas to, kas jau veikia. Tai nėra vienkartinis darbas – API dizainas yra nuolatinis balansas tarp lankstumo naujiems poreikiams ir stabilumo esamiems vartotojams.
Praktiškai, jei kuriate naują API šiandien, verta pradėti nuo trijų dalykų: apsibrėžti nuoseklų pavadinimų ir struktūros standartą prieš rašant pirmą liniją kodo, pasirinkti versijavimo strategiją iš anksto (net jei pirmoje versijoje ji atrodo perteklinė), ir investuoti į OpenAPI specifikaciją nuo pat pradžių, ne kaip vėlesnį priedą. Šie trys žingsniai sutaupys dešimtis valandų refaktoringo ir nervų ateityje – patikėkite, komandos, kurios tai ignoravo, dabar perrašo savo API antrą ar trečią kartą, ir tai kainuoja žymiai daugiau nei laikas, sutaupytas praleidus planavimo etapą.






