Kodėl verta kalbėti apie API architektūras 2024-aisiais
Jei dirbi su web aplikacijomis ar mobiliaisiais sprendimais, turbūt jau girdėjai šimtus ginčų apie tai, kuri API architektūra geresnė. REST API dominavo beveik du dešimtmečius, tačiau GraphQL atėjo su pažadu išspręsti visas skausmo vietas. Bet ar tikrai taip paprasta?
Realybė tokia, kad nėra vieno teisingio atsakymo. Yra projektai, kuriuose REST veikia puikiai, ir yra situacijos, kai GraphQL tampa tikru gelbėtoju. Problema ta, kad dauguma straipsnių šia tema arba pernelyg teoriniai, arba tiesiog kartoja dokumentaciją. Pabandysiu papasakoti apie skirtumus taip, kaip tai matau praktikoje – su realiais pavyzdžiais, pliusais ir minusais.
REST API: senas geras draugas su savo keistybėmis
REST (Representational State Transfer) egzistuoja nuo 2000-ųjų ir tapo facto standartu kuriant API. Pagrindinė idėja paprasta: naudoji HTTP metodus (GET, POST, PUT, DELETE) ir dirbi su resursais per URL endpoint’us.
Pavyzdžiui, jei nori gauti vartotojo duomenis:
GET /api/users/123
Jei nori atnaujinti tą vartotoją:
PUT /api/users/123
Skamba paprasta, ir iš tiesų taip yra. REST API lengva suprasti, dokumentuoti ir naudoti. Bet štai kur prasideda problemos: kas nutinka, kai tau reikia sudėtingesnių duomenų?
Tarkime, kuri mobilią aplikaciją ir tau reikia gauti vartotojo duomenis, jo paskutinius 5 įrašus ir komentarus po tais įrašais. Su REST API gali tekti daryti kelis užklausimus:
GET /api/users/123
GET /api/users/123/posts?limit=5
GET /api/posts/456/comments
GET /api/posts/457/comments
...
Tai vadinama N+1 problema, ir ji tikrai erzina. Alternatyva – sukurti specialų endpoint’ą tipo /api/users/123/dashboard, kuris grąžintų viską iš karto. Bet tada baigiesi su dešimtimis custom endpoint’ų, kurie tarnauja labai specifiniams use case’ams.
Kita problema – over-fetching. REST endpoint’as grąžina fiksuotą duomenų struktūrą. Jei tau reikia tik vartotojo vardo ir email’o, vis tiek gausi visą objektą su 20 laukų, įskaitant adresą, telefono numerį, gimimo datą ir t.t. Mobiliosiose aplikacijose, kur svarbus duomenų kiekis, tai gali būti skausminga.
GraphQL: naujasis vaikas rajone su ambicijomis
Facebook sukūrė GraphQL 2012-ais (viešai pristatė 2015-ais) būtent dėl šių REST problemų. Pagrindinė idėja revoliucinė: vietoj to, kad serveris diktuotų, kokius duomenis gausi, tu pats aprašai, ko tau reikia.
Tas pats pavyzdys su vartotoju GraphQL atrodytų taip:
query {
user(id: 123) {
name
email
posts(limit: 5) {
title
comments {
text
author {
name
}
}
}
}
}
Viena užklausa, visi duomenys. Gauni tiksliai tai, ko prašai – nei daugiau, nei mažiau. Skamba kaip magija, tiesa?
GraphQL turi vieną endpoint’ą (paprastai /graphql) ir viskas vyksta per POST užklausas. Naudoji specialią užklausų kalbą, kuri leidžia aprašyti sudėtingas duomenų struktūras ir ryšius. Schema apibrėžia, kokie duomenys prieinami ir kokių tipų jie yra.
Bet kaip ir su bet kuria technologija, čia yra savo „bet”. GraphQL nėra sidabrinė kulka, ir yra situacijų, kai jis sukuria daugiau problemų nei išsprendžia.
Kur GraphQL tikrai šviečia
Dirbu su vienu projektu, kur turime mobilią aplikaciją, web dashboard’ą ir admin panelę. Visi trys klientai naudoja tuos pačius duomenis, bet labai skirtingai. Mobilioji aplikacija rodo supaprastintą vaizdą, web dashboard’as – detalesnį, o admin panelė – viską iki paskutinio lauko.
Su REST API būtume sukūrę tris skirtingus endpoint’ų rinkinius arba vienas klientas gautų krūvą nereikalingų duomenų. GraphQL leido mums turėti vieną API, o kiekvienas klientas pasiima tik tai, ko jam reikia.
Kitas didelis privalumas – frontend kūrėjai gali dirbti beveik nepriklausomai. Jei jiems reikia naujo lauko, jie tiesiog prideda jį į užklausą (jei jis jau egzistuoja schemoje). Nereikia laukti, kol backend komanda sukurs naują endpoint’ą ar modifikuos esamą.
GraphQL Playground ar GraphiQL įrankiai yra fantastiniai. Gauni interaktyvią dokumentaciją, galimybę testuoti užklausas realiu laiku, autocomplete. Tai labai pagreitina development’ą ir onboarding’ą naujų komandos narių.
Dar vienas dalykas – versioning’as. Su REST API dažnai baigiesi su /api/v1/users, /api/v2/users ir t.t. GraphQL schema gali evoliucionuoti be breaking changes – tiesiog pažymi laukus kaip deprecated ir pridedi naujus.
Kur REST vis dar laimi
Tačiau nesuskubėkime nurašyti REST. Yra daug situacijų, kur jis vis dar geresnis pasirinkimas.
Pirma, paprastumas. Jei kuri paprastą CRUD aplikaciją su keliais resursais, REST yra tiesiog paprasčiau. Nereikia mokytis naujos užklausų kalbos, nereikia konfigūruoti sudėtingų resolver’ių, nereikia galvoti apie schema design’ą.
Antra, caching. HTTP caching su REST veikia out of the box. GET užklausos gali būti cache’inamos CDN lygmenyje, naršyklėje, tarpiniuose serveruose. Su GraphQL tai sudėtingiau, nes visos užklausos eina per POST į tą patį endpoint’ą. Yra sprendimų (persisted queries, Apollo Client cache), bet tai papildoma kompleksiškumo.
Trečia, failų upload’as. Su REST tai paprasta multipart form data užklausa. GraphQL nėra sukurtas failams – reikia naudoti papildomus sprendimus kaip Apollo Upload ar GraphQL Multipart Request spec.
Ketvirta, monitoring ir debugging. Su REST matai aiškius endpoint’us, status code’us, galima lengvai filtruoti logus. Su GraphQL visos užklausos eina į /graphql, reikia parsinti query body, kad suprastum, kas vyksta. Tai ne neįmanoma, bet reikalauja daugiau pastangų.
Performance klausimas: kas greičiau?
Čia dalykai tampa įdomūs. Teoriškai GraphQL turėtų būti efektyvesnis – viena užklausa vietoj kelių, tik reikalingi duomenys. Praktikoje ne visada taip išeina.
Problema vadinama N+1 query problem. Tarkime, prašai 100 vartotojų ir kiekvieno paskutinio įrašo. Naivus GraphQL resolver’is gali padaryti 1 užklausą vartotojams gauti ir 100 užklausų kiekvienam įrašui. Rezultatas – 101 database query vietoj 2.
Sprendimas – DataLoader pattern, kuris batch’ina užklausas. Bet tai reiškia, kad turi suprasti šią problemą ir ją išspręsti. Su REST API paprastai rašai SQL join’ą ir gauni viską viena užklausa.
Kita vertus, GraphQL leidžia išvengti over-fetching’o. Jei mobilė aplikacija prašo tik 3 laukų iš 30, tai realus bandwidth’o sutaupymas. Testuose mačiau 60-70% duomenų kiekio sumažėjimą mobiliose aplikacijose po migracijos į GraphQL.
Taip pat verta paminėti, kad GraphQL užklausos gali būti sudėtingos ir brangios. Blogai parašyta užklausa gali rekursyviai traukti duomenis ir užkrauti serverį. Reikia implementuoti query depth limiting, complexity analysis, rate limiting. REST API paprasčiau apsaugoti – kiekvienas endpoint’as turi aiškią kainą.
Realybė: hibridiniai sprendimai
Įdomus dalykas, kurį pastebėjau praktikoje – daugelis kompanijų nenaudoja tik vienos technologijos. Yra visai normalu turėti GraphQL API pagrindinei aplikacijos logikai ir REST endpoint’us specifiniams dalykams kaip webhook’ai, failų upload’as, ar trečiųjų šalių integracijoms.
Pavyzdžiui, GitHub API turi ir REST, ir GraphQL versijas. Stripe naudoja REST, bet pridėjo GraphQL wrapper’į kai kuriems use case’ams. Shopify pradėjo su REST, bet dabar aktyviai stumia GraphQL kaip pagrindinį API.
Taip pat matau tendenciją naudoti GraphQL kaip aggregation layer virš kelių REST API. Turi mikroservisus su REST endpoint’ais? Sukuri GraphQL gateway, kuris juos sujungia į vieną schema. Frontend’as dirba su GraphQL, backend’as lieka REST. Best of both worlds.
Kitas populiarus pattern’as – GraphQL tik frontend’ui, REST tarp servisų. Microservices komunikuoja tarpusavyje per REST (arba gRPC), bet išorinis API, kurį naudoja web ir mobile aplikacijos, yra GraphQL. Tai leidžia išlaikyti paprastumą backend’e ir duoti lankstumą frontend’ui.
Ką pasirinkti naujam projektui?
Gerai, užtenka teorijos. Jei pradedi naują projektą, kaip nuspręsti?
Rinkis REST, jei:
– Kuri paprastą CRUD aplikaciją su aiškiais resursais
– Tau svarbus caching ir CDN support
– Komanda neturi patirties su GraphQL
– Projektas nedidelis ir neturi sudėtingų duomenų ryšių
– Reikia public API, kurį naudos trečiosios šalys (REST vis dar labiau paplitęs)
– Dirbi su IoT ar embedded sistemomis, kur svarbus paprastumas
Rinkis GraphQL, jei:
– Turi kelis klientus (mobile, web, desktop) su skirtingais poreikiais
– Dažnai keičiasi frontend reikalavimai
– Turi sudėtingus duomenų ryšius ir nested struktūras
– Bandwidth’as yra kritinis (mobile aplikacijos)
– Nori stiprią type safety ir gerą developer experience
– Komanda pasiruošusi investuoti į mokymąsi ir tooling’ą
Praktinis patarimas: jei abejoji, pradėk su REST. Lengviau migruoti iš REST į GraphQL nei atvirkščiai. Galima pradėti su REST, o vėliau pridėti GraphQL layer virš esamo API. Yra įrankių kaip Hasura ar PostGraphile, kurie gali automatiškai generuoti GraphQL API iš egzistuojančios duomenų bazės.
Ateitis ir kas laukia už kampo
API pasaulis nestovi vietoje. Jau matome naujas tendencijas, kurios gali pakeisti žaidimą.
tRPC populiarėja TypeScript ekosistemoje – tai end-to-end type-safe RPC framework, kuris neturi runtime overhead. Jei tavo frontend ir backend abu TypeScript, tai gali būti įdomus pasirinkimas.
GraphQL Federation leidžia skaidyti schema į atskirus servisus, kurie vėliau sujungiami į vieną. Tai sprendžia vieną iš didžiausių GraphQL problemų – sunkumus skalėjant didelėse organizacijose.
REST API taip pat evoliucionuoja. JSON:API ir HAL standartai bando standartizuoti REST responses. OpenAPI (Swagger) specifikacija tobulėja ir leidžia generuoti klientus bei serverius automatiškai.
Bet svarbiausia pamoka, kurią išmokau per metus – technologija yra tik įrankis. Nesvarbu, ar naudoji REST, GraphQL, ar ką nors kita, jei nesupranti savo projekto poreikių ir vartotojų. Geriau padaryta REST API bus geresnė už blogai suprojektuotą GraphQL API.
Taip pat verta prisiminti, kad API architektūra nėra amžina. Technologijos keičiasi, projekto poreikiai auga. Tai, kas veikė su 10 vartotojų, gali neveikti su 10,000. Svarbiau sukurti lankstų sprendimą, kurį galima evoliucionuoti, nei ieškoti „tobulos” technologijos.
Galiausiai, abi technologijos turi savo vietą. REST neišnyks artimiausiu metu – per daug legacy sistemų, per daug žmonių jį žino, per daug įrankių jį palaiko. GraphQL taip pat čia liks – Facebook, GitHub, Shopify ir kiti gigantai investuoja į jį rimtai. Geriausia, ką gali padaryti – išmokti abi technologijas ir suprasti, kada naudoti kurią.
