Pragmatičan dizajn API-ja: Izgradnja za promjene, a ne samo za danas
API je obećanje dano u kodu. Jednom kada druga usluga, mobilna aplikacija, partner ili interni tim ovisi o njemu, promjena tog obećanja postaje mnogo skuplja od dodavanja još jedne krajnje točke. Teži dio nije stvaranje JSON-a danas. Radi se o oblikovanju granica koje sustavu omogućuju razvoj bez prisiljavanja svakog korisnika da se kreće istim tempom.
Pragmatično oblikovanje API-ja počinje jednostavnim načinom razmišljanja: optimizirajte za razumljivu promjenu. To znači birati konvencije koje su dovoljno jednostavne da budu predvidljive, dovoljno eksplicitne da budu sigurne i dovoljno fleksibilne da prihvate zahtjeve s kojima se još niste susreli.
Oblikujte oko mogućnosti, a ne oko tablica baze podataka
Česta rana pogreška jest izravno izlaganje modela pohrane. Ako baza podataka ima tablice orders, order_items i customers, može se činiti prirodnim stvoriti odgovarajuće CRUD krajnje točke. Takav je pristup brz, ali API tada nasljeđuje svaku odluku o pohrani.
Korisnike obično ne zanima kako su podaci normalizirani. Zanimaju ih mogućnosti: naručivanje, pregled statusa dostave, otkazivanje pod valjanim uvjetima ili preuzimanje računa. To su korisni API koncepti jer mogu ostati stabilni čak i kada se promijene shema, model rada s redovima čekanja ili granice internih usluga.
Na primjer, prikaz narudžbe može sadržavati polja koja klijent treba, bez zrcaljenja svakog stupca:
{
"id": "ord_123",
"status": "processing",
"total": {
"amount": "49.90",
"currency": "EUR"
},
"customer": {
"id": "cus_456",
"name": "Avery Chen"
}
}
Interna implementacija može kasnije podijeliti podatke o kupcima u drugu uslugu ili drukčije izračunavati ukupne iznose. Ako javni ugovor ostane promišljen, korisnici to ne bi trebali trebati znati.
Učinite ugovore eksplicitnima i predvidljivima
Dosljednost je jedna od najvrjednijih značajki koje API može ponuditi. Odaberite konvencije za imenovanje, datume, identifikatore, paginaciju, odgovore na pogreške i nullable polja, a zatim ih primjenjujte svugdje. Korisnik ne bi trebao trebati novo tumačenje za svaki resurs.
Rano odlučite upotrebljavaju li JSON svojstva snake_case ili camelCase. Upotrebljavajte jedan format datuma, po mogućnosti nedvosmisleni prikaz ISO 8601 kada je vrijeme važno. Namjerno prikazujte novac: vrijednosti s pomičnim zarezom pozivaju na suptilne pogreške, stoga je iznos kao decimalni niz ili cijeli broj u manjim novčanim jedinicama često sigurniji kada se dosljedno dokumentira.
Odgovori na pogreške zaslužuju jednaku pažnju kao i uspješni odgovori. Generička pogreška poslužitelja ponekad je neizbježna, ali pogreške validacije i domene trebaju omogućiti poduzimanje radnje.
{
"error": {
"code": "invalid_state_transition",
"message": "An order can only be cancelled before shipment.",
"details": {
"current_status": "shipped"
}
}
}
HTTP status prenosi široku kategoriju neuspjeha. Stabilni kod pogreške omogućuje klijentima donošenje odmjerene odluke. Poruka pomaže razvojnom programeru dijagnosticirati problem. Izbjegavajte da klijenti moraju analizirati prozu i ne izlažite stack traceove, SQL poruke ni pojedinosti interne infrastrukture.
Koristite HTTP semantiku bez doktrinarnosti
REST konvencije korisne su jer smanjuju iznenađenja. GET ne bi trebao mijenjati stanje. POST obično stvara resurs ili pokreće proces. PATCH dobro odgovara djelomičnim ažuriranjima. Statusni kodovi trebaju odražavati ishod, a ne samo potvrditi da se izvršio aplikacijski kod.
No pragmatičnost je važnija od prisiljavanja svake poslovne radnje u URL oblikovan kao imenica. Neke su operacije radnje s pravilima, nuspojavama i asinkronim radom. Krajnja točka poput POST /orders/ord_123/cancel može biti jasnija od dvosmislenog djelomičnog ažuriranja kada otkazivanje pokreće vraćanje zaliha, obradu plaćanja i obavijesti.
Važno je pitanje čini li krajnja točka ponašanje domene očitim. Uredan URL nije zamjena za pouzdan ugovor.
Planirajte ponovne pokušaje prije nego što proizvodnja poduči lekciju
Mreže otkazuju na nezgodne načine. Klijent može poslati zahtjev, izgubiti odgovor i ponoviti pokušaj iako je poslužitelj dovršio izvornu operaciju. To je posebno opasno za operacije koje stvaraju plaćanja, narudžbe, pozivnice ili vanjske nuspojave.
Za operacije stvaranja koje se mogu ponoviti, podržite ključ idempotentnosti. Klijent generira jedinstveni ključ i šalje ga uz zahtjev; poslužitelj pohranjuje ključ uz rezultirajuću operaciju i vraća isti ishod za odgovarajući ponovljeni pokušaj. U PHP-u implementacija treba provjeru ključa i trajno stvaranje poslovnog zapisa učiniti dijelom jedne pažljivo oblikovane granice transakcije.
Nemojte idempotentnost tretirati kao zaglavlje koje možete dodati kasnije bez oblikovnog rada. Definirajte što dva zahtjeva čini ekvivalentnima, koliko se dugo ključevi zadržavaju i što se događa ako se isti ključ ponovno upotrijebi s drukčijim sadržajem zahtjeva. Vraćanje jasnog odgovora o sukobu sigurnije je od tihog primjenjivanja neočekivane operacije.
Verzionirajte štedljivo i razvijajte aditivno
Verzioniranje nije dozvola za olako objavljivanje promjena koje narušavaju kompatibilnost. Nova glavna verzija stvara operativni posao: dokumentacija se razilazi, klijenti migriraju različitim brzinama, matrice testiranja rastu, a za staro ponašanje potreban je plan povlačenja.
Prednost dajte kompatibilnim dodacima kada je moguće. Dodavanje neobaveznog polja odgovora često je sigurno. Dodavanje novog neobaveznog parametra upita obično je sigurno. Uklanjanje polja, promjena njegova tipa, redefiniranje vrijednosti ili promjena ponašanja paginacije nije sigurno.
Kada je promjena koja narušava kompatibilnost nužna, učinite je vidljivom i ograničenom. Putanja poput /v2/orders lako se otkriva i usmjerava, dok verzioniranje putem zaglavlja može održati URL-ove čišćima, ali zahtijeva bolje alate i dokumentaciju. Oba izbora mogu funkcionirati. Važno je imati jasnu politiku kompatibilnosti, smjernice za migraciju i naveden postupak zastarijevanja.
Paginacija, filtriranje i izvedba pitanja su ugovora
Krajnja točka koja radi nad deset redaka može postati produkcijski incident nad deset milijuna. API-ji zbirki trebaju ograničenja od početka. Upotrebljavajte dokumentiranu maksimalnu veličinu stranice, deterministički redoslijed i format odgovora koji korisnicima govori kako nastaviti.
Paginacija kursorom često je dobar izbor za velike zbirke ili zbirke koje se često mijenjaju jer izbjegava nestabilnost i rastući trošak koje može uvesti paginacija temeljena na pomaku. Također zahtijeva pažljivo uređivanje i indeksiranje. Ako krajnja točka sortira prema created_at i upotrebljava identifikator za razrješavanje jednakih vrijednosti, baza podataka trebala bi imati indeks koji podržava taj obrazac pristupa.
I filtriranje treba ograničenja. Izlaganje proizvoljnih filtara polja ili izraza upita nalik bazi podataka znatno otežava kontrolu autorizacije, validacije i izvedbe. Ponudite filtre koji odgovaraju stvarnim potrebama korisnika, validirajte njihove vrijednosti i dokumentirajte njihovu interakciju sa sortiranjem i paginacijom.
Neka PHP granica bude tanka
U PHP pozadini kontroleri bi trebali prevoditi HTTP u pozive aplikacije, a ne postati mjesto na kojem se gomilaju pravila domene. Validirajte oblik zahtjeva na rubu, autorizirajte aktera, pozovite aplikacijsku uslugu ili obrađivač naredbi te preslikajte rezultat u odgovor.
To razdvajanje donosi korist kada isto ponašanje kasnije treba pokrenuti iz radnika reda čekanja, zadatka naredbenog retka ili drugog API-ja. Također čini testove korisnijima: pravila domene mogu se testirati bez izgradnje HTTP zahtjeva za svaki slučaj, dok se testovi krajnjih točaka usredotočuju na usmjeravanje, serijalizaciju, autentikaciju i statusne kodove.
Slično tome, oduprite se dopuštanju da ORM entiteti postanu vaši javni objekti odgovora. Namjenski modeli zahtjeva i odgovora stvaraju malu količinu posla preslikavanja, ali sprječavaju slučajno izlaganje polja i odvajaju razvoj API-ja od promjena u pohrani.
Dokumentirajte ponašanje, a zatim provjerite dokument
Dokumentacija je dio proizvoda, a ne naknadna misao. Opišite autentikaciju, potrebne dozvole, polja zahtjeva, oblike odgovora, kodove pogrešaka, pravila paginacije i ponašanje ponovnih pokušaja. Primjeri pomažu, ali sami primjeri nisu ugovor.
Format sheme može pružiti korisnu strukturu, osobito kada pokreće generiranje klijenata ili testove ugovora. Držite ga blizu implementacije i pobrinite se da automatizirani testovi provjeravaju važne pretpostavke. Dokumentirano polje koje poslužitelj nikada ne šalje obmanjujuće je; nedokumentirano polje koje klijenti počnu upotrebljavati postaje slučajna obveza.
Gradite API-je koji ostavljaju prostor za razmišljanje
Najbolji API rijetko je onaj s najviše apstrakcije ili najmanje krajnjih točaka. To je onaj koji korisnicima pomaže obaviti stvarni posao, a istodobno čuva sposobnost tima da odgovorno mijenja sustav.
Učinite uobičajeni put jasnim. Učinite neuspjehe razumljivima. Učinite ponovne pokušaje sigurnima. Držite pojedinosti pohrane privatnima i svaki odgovor tretirajte kao obećanje s troškom održavanja. Kada promjena dođe — a uvijek dođe — pragmatični API pretvara je iz koordinirane hitne situacije u uobičajeni inženjerski posao.