Hogyan készítsünk jó API-t

#api #rest

Hogyan készítsünk jó API-t

Mi tesz jóvá egy API-t és mit tudunk tenni, hogy az általunk készített backend is ebbe a kategóriába essen? Ugorjunk egy fejest a témába és vegyük végig milyen dolgokat kell szem előtt tartanunk háttér rendszerünk fejlesztése közben.

Smoke and mirrors

Sajnos fordult már elő velem, hogy egy nagyon dokumentált és borzasztó modern API integrálása és használata pain-in-the-a** lett mire elértünk a live deploy-ig. Ilyenkor látszik nagyon, hogy több dologra is oda kell figyelni ha külső rendszereket akarunk kiszolgálni.

Íme a bűvös checklist, amit mindenkinek kötelező lenne követnie:

  1. Tervezés
  2. Dokumentáció
  3. Integráció
  4. Verziókövetés
  5. Sandbox
  6. Biztonság
  7. Hibakezelés
  8. Uptime és sebesség
  9. Support

De vegyük is végig ezeket a pontokat és nézzük meg mit takarnak.

Tervezés

Mindig gondoljuk végig mik az igényeink és milyen technológiákkal akarjuk ezt megvalósítani. Ha speciális ügyfélkör számára készül a backend, akkor vegyük figyelembe az Ő igényeiket is a tervezés során.

A tervezés első fázisában készítsünk egy vázlatos szerkezetet, amiben jelöljük a különböző rendszerösszetevőket. Javaslom első körben rajzoljátok le egy whiteboard-ra és beszéljétek át egy kollégával a tervet. A pozitív inputok után mélyítsük ötleteinket. Tervezzük meg az adatbázisokat és a rendszerkapcsolatokat is, majd kezdjük el részletezni az interfészeket és azok funkcióját.

Már ilyenkor gondoljunk a terhelésre! Mekkora adatmennyiséget kell kiszolgálni? Milyen gyors választ szeretnénk elérni egyes interfészeken? Előnyünkre válhat ha már ebben a fázisban tudjuk, hogy hol lesz szükség cache-elésre és hogyan kell előkészíteni az adatokat a kívánt sebesség eléréséhez.

Let there be light

Ha a vázlatunkkal elkészültünk, akkor elkezdhetjük interfészenként végiggondolni a rendszer működését. A CRUD megvalósításánál és GET paraméterek kialakításánál kövessük a REST logikát és következő egyszerű képletet.

GET /megrendelesekLekérdezzük a megrendelések listáját
GET /megrendelesek/3232-es ID: megrendelés részletei
POST /megrendelesekÚj megrendelés hozzáadása
PUT /megrendelesek/3232-es ID: megrendelés módosítása
DELETE /megrendelesek/3232-es ID: megrendelés törlése

Listák lekérdezésénél vagy keresésre dedikált interfésznél ha lehetséges mindig adjunk lehetőséget az adatok filterezésére, összetételük módosítására és a visszatérési sorrend definiálására. A fenti példánkra visszatérve - ha csak az aktív státusszal rendelkező megrendelések azonosítóit akarjuk lekérdezni, akkor velószínűleg nem érdekelnek minket a rendelés további részletei, sem a nem aktív státusszal rendelkező megrendelések. Így ha mondjuk ezt az API-val el tudjuk érni egy ?status=1&build=id_only paraméter sorozattal, akkor ez nagyban növeli a használhatóságát rendszerünknek. Nem is beszélve arról, hogy ha a felhasználók tudják módosítani a visszatérő adatok összetételét, hogy csak azokat a mezőket tartalmazzák amire szükségük van, akkor ezzel potenciálisan gyorsítani és optimalizálni tudják a válaszok sebességét. (bonyolult node-ok esetén ez nagyon hasznos lehet)

Ha létrehozunk vagy aktualizálunk valamilyen tartalmat, akkor adjuk vissza a válaszban az aktuális node tartalmát. Tehát ha készítünk egy megrendelést, akkor adjuk vissza annak részleteit a sikeres válaszban. Ha pedig módosítjuk a megrendelést, akkor szintén tegyük ugyanezt. Nem is gondolnánk, de ilyenkor megspórolhatunk egy plusz lekérdezést a kliens oldalon.

Felejtsük el az XML-t. Minden modern API JSON alapú, és ha építünk egyet, akkor használjuk ezt az adatszerkezetet a kommunikációhoz. Sajnos előfordulhat, hogy őskövület rendszereket kell kiszolgálnunk XML-el. Ilyenkor azt javaslom készítsünk két verziót az API-ból és az URL-ben tegyük definiálhatóvá, hogy XML vagy JSON adatsémát akarunk használni. Ezzel megkönnyítjük azok számára az átállást, akik a jövőben migrálni szeretnék a rendszerüket.

Dokumentáció

Egy dokumentáció már egy jó specifikációval félig el is készült. Hiszen ha minden meg van tervezve és az alapján történik a rendszer megvalósítása is, akkor már csak át kell csoportosítani az információkat a belső használatra szánt specifikációból a külső használatra készülő dokumentációba. Persze sose felejtsük el a specifikációt aktualizálni ha a fejlesztés közben valamin módosítani kellene, mert ez igen gyakran megtörténik.

Belső dokumentációk számára a verziószámozott dokumentumok is megfelelőek. Viszont egy külső használatra szánt API dokumentáció számára a webes forma a javasolt. Sokkal visszakövethetőbben kezelhetjük a dokumentum és az API változásait. Verziókezelőben érdemes külön kezelni az oldalunk többi részétől és akár push-to-deploy módszertan alapján is működtethetjük azt.

Számtalan API dokumentációra kitalált HTML template-et találhattok az interneten. Számomra mindig a one-page megoldások sidebar navigációval jöttek be a legjobban. Tudom ajánlani a Jekyll-t is erre a célra, ami ezt az oldalt is hajtja. Így akár közvetlen Githubon is host-olhatjuk az API-nk dokumentációját, ha ezt szeretnénk.

Javasolt dokumentum szerkezet egy átlagos API számára:

  1. Funkcionális áttekintés és folyamatábrák
  2. Authentikáció és biztonság
  3. Interfészek példákkal
  4. Best practices
  5. Validációs folyamat (zárt API esetén)
  6. Changelog

Integráció

Fontos, hogy legyen egy működő és dokumentált integrációs folyamatunk zárt API esetén. Ennek megtervezésére készítsünk egy folyamatábrát. Vegyük végig és adjuk hozzá a lépéseket a fejlesztői hozzáférés megadásától az éles hozzáférés engedélyezéséig.

Zárt rendszer esetén általában a következő folyamatlépésekre kell gondolnunk:

  1. Szerződéskötés és fejlesztői környezet hozzáférésének engedélyezése
  2. Support iterációk a fejlesztés közben
  3. Integrálás validálása, teljesítendő tesztesetek definiálása (ezek ellenőrzését érdemes automatizálni)
  4. Éles rendszer hozzáférésének engedélyezése

Verziókövetés

Egy API esetén nagyon fontos, hogy rugalmas és innovációra képes felületünk legyen, de ne törjük ketté a már integrált rendszerek funkcionalitását az új funkciók élesítésével. Perszem meg kell klüönböztetnünk az un. ‘breaking changes’ és a ‘non-breaking changes’ kategóriába tartozó változtatásokat.

Az esetleges bonyodalmak elkerülése érdekében mindig verzió számozzuk a rendszerünk. Breaking changes esetén pedig ezt a verziószámot tüntessük fel az API URL-jében. Pl: api.foobar.com/api/v2/endpoint Így aki még nem készült fel a legújabb fejlesztések használatára, annak időt adunk, hogy átvegye az új módosításokat.

Javasolt verzióléptetés esetén a dokumentációt is klónozni és tiszta lappal vezetni tovább az új verzió leírását. Gyakran raknak egy verzió választó lenyílót az online dokumentumokba, ezzel jelölve melyik verzió leírását böngésszük éppen. Minden esetben kerüljük az összemosott dokumentációt, ahol csak egy dokumentum létezik és megjegyzéseket írunk az interfész leírása után, mint például: “v1-ben 2 paramétert kér, de v2-ben már 4 szükséges neki..”. Ez alól kivétel lehet ha valami @depreciated lesz egy új API verzió alatt. Ezt nyugodtan jelölhetjük a régebbi verziók leírásánál.

Sandbox

Ha bonyolultabb auth-ot raktunk az API-nk elé és nem tesztelhető könnyedén a böngésző URL-jéből az API, akkor nagyon hasznos ha egy sandbox felületet is biztosítunk a kliensek számára. Lehet ez személyre szabott, ami teljesen a mi kulcsainkat használja és az alapján szimulálja az API használatot. De lehet teljesen általános is, mint például az olyan API-k, ahol ha a szerver interfész URL-jére navigálunk a böngészőben, akkor rögtön az adott interfész sandbox felületére kerülünk.

Biztosítsunk egy kattintásra beilleszthető példákat! Nehéz használni az olyan sandbox felületeket ahol csak egy üres textarea van megadva, és keresgetnünk kell milyen adatszerkezettel és paraméterekkel lehet használni az adott végpontot.

Form-ban szerkeszthetőek legyenek a GET paraméterek! Felesleges ugyanúgy keresgélni és az URL-ben szerkeszteni a paramétereket, ha ezt egy magyarázattal ellátott form-ban is meg tudjuk jeleníteni. Így könnyű értelmezni és megtanulni a szükséges paramétereket.

AJAX-al kezelje a hívásokat és jelenítse meg őket az oldalon! Ne legyen szimpla POST és ne a raw inputot jelenítsük meg, hanem formázzuk meg és tegyük olvashatóvá vagy letölthetővé az API válaszát.

Ne felejtsük el a fejléceket sem! A kérés és a válasz megjelenítésénél mutassuk meg kérés és a válasz fejléceit is. Ez nagyon hasznos lehet, főleg ha vannak kötelező fejlécek is az interfésznél.

Biztonság

Ebbe nem mennék bele nagyon részletesen, hiszen már írtam múlt hónapban egy cikket ebben a témában. De a lényeg, hogy mérlegeljük milyen szintű és típusú biztonságot szeretnénk és tegyük egyértelművé a felhasználók számára hogyan tudnak authentikálni vagy token-t kérni a rendszerünkben.

Ha mélyebben szeretnél tájékozódni a témában, akkor ajánlom figyelmedbe cikkemet: Beszéljünk az API biztonságról

Hibakezelés

Lényeges része a rendszerünknek az, hogy miként kezeljük a hibákat. Nem elég logolni és automatizált reportokat írni a rendszerben keletkező hibákról, hanem a megfelelő visszajelzést is kell adnunk a felhasználóknak, hogy magunktól javítani tudják a problémát. Ennek elmulasztása esetén sok support megkeresésre számíthatunk, amit mindig érdemes elkerülni.

Használjuk a HTTP státusz kódokat a válaszainkban. Mind a 400-as (kliens hiba) és az 500-as (szerver hiba) hibatípusok részletes kezelésével egyértelmű hibaüzeneteket adhatunk tovább a kliens felé. Például ha teáscsészék vagyunk és kávét akarnak tőlünk: 418 - I’m a teapot

Uptime és sebesség

Persze a világ legjobb API-ja sem ér semmit, ha nem elérhető. Ezért folyamatosan monitorozzunk rendszerünk elérhetőségét és leterheltségét.

Érdemes egy olyan architektúrát választanunk ami scalable, ha szükség lenne rá.

Személy szerint én az AWS rendszerével jó tapasztalatokat szereztem, de a DigitalOcean alatt is sikerült mindig elérni azt amit szerettem volna. Így ezeket a felhő alapú megoldásokat jó szívvel tudom ajánlani.

Support

API méretétől függően érdemes egy ‘issue tracking’ vagy ‘ticketing’ rendszer alá helyezni a supportunkat. Ilyenkor egy központi email címre érkeznek a megkeresések amiket a rendszer ellát egy aozonsítóval. A fejlesztők vagy a support menedzserek közül az arra jogosult leveheti magához az ügyet vagy kioszthatja a feladatot másnak is.

Közvetlen API support esetén próbáljunk 24 órán belül válaszolni a megkeresésekre, hiszen ha valami probléma van, akkor a gyors megoldás létfontosságú lehet.

Összefoglaló

Tervezzünk és dokumentáljunk mielőtt és miközben fejlesztenénk. Tegyük minél egyszerűbbé az integrációt és a validálás folyamatát. Legyünk elérhetőek és hibamentesek. Ha mégis hiba keletkezne, akkor kezeljük azt gyorsan és átláthatóan.

De bármennyire is eltervezzük, dokumentáljuk és karbantartjuk rendszerünk, problémák mindig adódhatnak. (Murphy’s law) Ugyanakkor ha felkészülünk ezekre és a hibák kezelésére, akkor nem csak az integráció, hanem a rendszer használata is zökkenőmentes lesz a kliensek számára.

További tartalmak