Kodėl apskritai kalbame apie API pasirinkimą
Kai kuriate naują projektą ar planuojate modernizuoti esamą sistemą, vienas iš svarbiausių sprendimų – kaip organizuoti komunikaciją tarp kliento ir serverio. Dažniausiai pasirinkimas krenta tarp dviejų pagrindinių variantų: tradicinio REST arba vis populiarėjančio GraphQL. Šis klausimas nėra vien techninis – jis turi įtakos komandos produktyvumui, sistemos našumui ir ilgalaikei priežiūrai.
REST API jau seniai tapo standartu. Beveik kiekvienas programuotojas bent kartą su juo susidūrė, o daugelis įmonių turi gerai įsitvirtinusias praktikas. Tačiau GraphQL, kurį 2015 metais viešai pristatė Facebook, siūlo kitokį požiūrį į duomenų keitimąsi. Klausimas nėra „kuris geresnis”, o veikiau „kuris labiau tinka konkrečiai situacijai”.
REST principai ir jų stipriosios pusės
REST (Representational State Transfer) veikia pagal gana paprastą logiką – turite resursus, kuriuos pasiekiate per URL, ir naudojate HTTP metodus (GET, POST, PUT, DELETE) jiems valdyti. Pavyzdžiui, /api/users/123 grąžina vartotoją su ID 123, o /api/posts – visų įrašų sąrašą.
Didžiausias REST privalumas – paprastumas ir nuspėjamumas. Kai komandoje dirba naujas žmogus, jam nereikia ilgai aiškinti, kaip veikia sistema. Matai endpoint’ą /api/products, iš karto supranti, kas ten vyksta. Be to, REST puikiai integruojasi su HTTP cache mechanizmais – galite naudoti CDN, reverse proxy ir kitas standartines technologijas be jokių papildomų triukų.
Dar vienas svarbus momentas – debugging’as. Kai kažkas neveikia, galite tiesiog nukopijuoti URL į naršyklę ar Postman ir pamatyti, ką grąžina serveris. Nereikia jokių specialių įrankių ar sudėtingų query sintaksių. Tai ypač svarbu, kai sprendžiate produkcinę problemą 3 valandą nakties.
Tačiau REST turi ir akivaizdžių trūkumų. Vienas didžiausių – over-fetching ir under-fetching problema. Tarkime, jums reikia vartotojo vardo ir el. pašto, bet /api/users/123 grąžina visą objektą su adresu, telefonu, nustatymu sąrašu ir dar dešimčia laukų, kurių jums nereikia. Arba atvirkščiai – norite gauti vartotoją su jo paskutiniais įrašais ir komentarais, bet tai reikalauja trijų atskirų užklausų.
GraphQL filosofija ir architektūra
GraphQL veikia fundamentaliai kitaip. Vietoj daugybės endpoint’ų turite vieną, paprastai /graphql, ir klientas tiesiogiai nurodo, kokių duomenų jam reikia. Tai kaip SQL užklausa, tik ne duomenų bazei, o API.
Štai paprastas pavyzdys:
query {
user(id: "123") {
name
email
posts(limit: 5) {
title
createdAt
}
}
}
Ir gausite tiksliai tai, ko prašėte – nei daugiau, nei mažiau. Jokių papildomų laukų, jokių atskirų užklausų įrašams gauti. Viskas viename response.
GraphQL schema veikia kaip kontraktas tarp frontend ir backend komandų. Ji aprašo, kokie tipai egzistuoja, kokie laukai jiems prieinami, kokie argumentai galimi. Tai labai padeda didelėse komandose, kur frontend ir backend programuotojai dirba lygiagrečiai. Schema tampa vienintele tiesos šaltiniu.
Dar vienas įdomus dalykas – introspection. GraphQL API gali papasakoti apie save – kokius tipus palaiko, kokius laukus galite užklausti. Tai leidžia sukurti tokius įrankius kaip GraphiQL ar Apollo Studio, kurie suteikia auto-complete, dokumentaciją ir debugging galimybes iš dėžės.
Našumo aspektai realybėje
Teoriškai GraphQL turėtų būti greitesnis – juk siunčiate mažiau duomenų ir darote mažiau užklausų. Praktikoje viskas sudėtingiau.
REST API su gerai sukonfigūruotu caching gali būti neįtikėtinai greitas. Jei turite endpoint’ą /api/products, galite jį cache’inti CDN lygyje, ir milijonai užklausų net nepasieks jūsų serverio. Su GraphQL tai daug sudėtingiau, nes kiekviena užklausa gali būti unikali.
Tačiau GraphQL sprendžia N+1 problemą elegantiškiau. REST pasaulyje, jei norite gauti 10 vartotojų su jų profilių nuotraukomis, dažnai baigiasi tuo, kad darot 11 užklausų (viena vartotojų sąrašui, po vieną kiekvienam profiliui). GraphQL su DataLoader ar panašiais įrankiais gali batch’inti tokias užklaugas automatiškai.
Realus pavyzdys iš praktikos: mobilios aplikacijos, kur tinklo ryšys lėtas ir nestabilus, labai gauna naudos iš GraphQL. Vietoj penkių-šešių REST užklausų, kurios gali užtrukti sekundes, viena GraphQL užklausa grąžina viską, ko reikia. Tai jaučiasi kaip diena ir naktis vartotojo patirties požiūriu.
Kūrimo patirtis ir įrankiai
Kai pradedi dirbti su GraphQL, pirmą savaitę jauti, kad viskas per sudėtinga. Reikia apibrėžti schemas, resolvers, galvoti apie tipus. REST atveju tiesiog sukuri endpoint’ą ir grąžini JSON – padaryta.
Bet po kelių savaičių situacija apsiverčia. Frontend programuotojai pradeda džiaugtis, kad gali gauti tiksliai tai, ko reikia, be derybų su backend komanda dėl naujų endpoint’ų. Backend programuotojai vertina, kad schema verčia juos galvoti apie duomenų struktūrą sistemiškai.
Apollo Client ar Relay frontend’e suteikia daug automatizacijos – cache’inimą, optimistic updates, error handling. Tai veikia iš dėžės ir veikia gerai. REST pasaulyje dažnai tenka visa tai rašyti patiems arba kombinuoti įvairius įrankius.
Tačiau debugging’as gali būti sudėtingesnis. Kai GraphQL užklausa grąžina klaidą, ne visada akivaizdu, kuris resolver’is kaltininkas. Reikia gerų logging įrankių ir praktikos. REST atveju paprastai aišku – šitas endpoint’as neveikia, žiūrim į tą controller’į.
TypeScript integracija su GraphQL yra fantastinė. Įrankiai kaip GraphQL Code Generator gali automatiškai sugeneruoti tipus iš schemos. Tai reiškia, kad jūsų IDE žino, kokie laukai prieinami, ir gaunate auto-complete bei type checking visame kelyje nuo užklausos iki UI. Su REST dažnai tenka tipus rašyti rankomis arba naudoti Swagger/OpenAPI, kas veikia, bet ne taip sklandžiai.
Versioning ir API evoliucija
REST pasaulyje versioning’as yra amžina problema. Turite /api/v1/users, paskui /api/v2/users, ir staiga palaikote dvi ar tris versijas vienu metu. Klientai lėtai migruoja, seni endpoint’ai lieka gyvi metų metus, kodas tampa legacy šlamštu.
GraphQL siūlo kitą požiūrį – evoliuciją be versijų. Vietoj to, kad keistumėte esamą funkcionalumą, pridedame naujus laukus, o senus pažymime kaip deprecated. Klientai gali migravoti savo tempu, o jūs matote, kurie deprecated laukai dar naudojami, ir galite planuoti jų pašalinimą.
Praktiškai tai atrodo taip: turite lauką phoneNumber, bet suprantate, kad geriau būtų phone objektas su countryCode ir number. GraphQL atveju pridedame naują phone lauką, seną pažymime deprecated, ir frontend komanda gali migravoti per keletą sprint’ų. REST atveju greičiausiai reikėtų /api/v2.
Tačiau reikia disciplinos. Jei komanda neturi geros praktikos dokumentuoti deprecated laukus ir sekti jų naudojimą, galite baigtis su schema, kuri auga be galo. Tai kaip ir su bet kokia technologija – įrankis geras, bet reikia procesų.
Saugumo klausimai ir apribojimai
REST API saugumas yra gerai išnagrinėtas. Žinome, kaip daryti rate limiting pagal endpoint’us, kaip cache’inti, kaip apsaugoti nuo įprastų atakų. GraphQL čia kelia naujų iššūkių.
Viena didžiausių problemų – query complexity. Klientas gali parašyti užklausą, kuri rekursyviai klausia duomenų ir užkrauna serverį:
query {
users {
posts {
comments {
author {
posts {
comments {
...
}
}
}
}
}
}
}
Tai gali tapti denial-of-service ataka, net jei netyčia. Reikia įdiegti query complexity analizę ir nustatyti limitus. Įrankiai kaip graphql-query-complexity padeda, bet tai papildomas dalykas, apie kurį reikia galvoti.
Rate limiting taip pat sudėtingesnis. REST atveju galite apriboti pagal endpoint’ą – 100 užklausų per minutę į /api/users. GraphQL atveju visos užklausos eina į vieną endpoint’ą, tai reikia skaičiuoti pagal query complexity arba kitus metriką.
Authorization’as gali būti ir paprastesnis, ir sudėtingesnis. Paprastesnis, nes galite implementuoti teisių tikrinimą resolver’io lygyje – kiekvienas laukas gali turėti savo teisių logiką. Sudėtingesnis, nes reikia užtikrinti, kad nepraleistumėte jokio lauko ir neatskleisite duomenų per nested užklausas.
Kada rinktis vieną ar kitą
Jei kuriate viešą API, kurį naudos trečiosios šalys, REST dažnai yra saugesnis pasirinkimas. Jis paprastesnis dokumentuoti, lengviau suprasti naujiems naudotojams, ir yra daugiau įrankių bei bibliotekų įvairiose platformose. GraphQL gali atrodyti per sudėtingas, jei jūsų API naudotojai tik nori gauti produktų sąrašą ar sukurti užsakymą.
Mobilios aplikacijos ir SPA (Single Page Applications) labai gauna naudos iš GraphQL. Galimybė gauti visus reikiamus duomenis viena užklausa ir tiksliai nurodyti, ko reikia, sutaupo tinklo resursų ir pagerina UX. Jei kuriate React ar Vue aplikaciją su sudėtingu state management, Apollo Client ar urql gali labai supaprastinti gyvenimą.
Microservices architektūroje GraphQL gali veikti kaip API gateway. Turite dešimt skirtingų servisų su REST API, bet frontend’ui reikia duomenų iš kelių jų vienu metu. GraphQL sluoksnis gali agregavoti šias užklausas ir pateikti vieningą interface. Tačiau tai prideda dar vieną complexity sluoksnį.
Jei jūsų komanda maža ir neturi daug patirties, REST leis pradėti greičiau. GraphQL reikalauja daugiau pradinių investicijų į mokymąsi ir setup’ą. Bet jei planuojate augti ir numatote, kad API taps sudėtingas, GraphQL gali atsipirkti ilguoju laikotarpiu.
Legacy sistemų atveju REST dažnai lieka, nes migracija būtų per brangi. Bet galite pradėti nuo GraphQL sluoksnio virš esamų REST endpoint’ų – tai gana populiarus pattern’as. Apollo Server turi įrankių, kurie leidžia wrap’inti REST API į GraphQL schema.
Ką daryti su visa šia informacija
Technologijų pasirinkimas niekada nėra juodas ar baltas. REST ir GraphQL abu turi savo vietą, ir kartais net prasminga naudoti abu viename projekte – REST paprastoms CRUD operacijoms, GraphQL sudėtingesnėms užklausoms su daug ryšių.
Svarbiausia – suprasti savo poreikius. Jei jūsų API paprastas, klientų nedaug, ir jie daro standartinius dalykus, REST puikiai veiks dar daugelį metų. Jei kuriate platformą su sudėtingais duomenų ryšiais, mobilias aplikacijas, arba turite didelę frontend komandą, kuri nuolat prašo naujų endpoint’ų, GraphQL gali būti žaidimo keitėjas.
Nebijokite eksperimentuoti. Galite pradėti nuo mažo – sukurti vieną GraphQL endpoint’ą šalia esamų REST, pamatyti, kaip komandai sekasi, ar tai sprendžia realias problemas. Technologijos turi tarnauti jums, ne atvirkščiai. Kartais geriausias sprendimas yra tas, kurį jūsų komanda geriausiai supranta ir gali efektyviai palaikyti, net jei jis nėra „moderniausias” ar „populiariausias”.
