Pradžia / Programavimas / Rest vs GraphQL

Rest vs GraphQL

Kas čia vyksta ir kodėl tai svarbu

Jei dirbai su bet kokia šiuolaikine web aplikacija, tikrai susidūrei su klausimu: kaip frontend’as kalbasi su backend’u? Ir čia prasideda įdomi diskusija, kuri IT bendruomenėje vyksta jau gerą dešimtmetį. REST ir GraphQL – du skirtingi požiūriai į tą pačią problemą, ir kiekvienas iš jų turi savų privalumų bei trūkumų, kuriuos verta suprasti prieš renkantis.

Šis straipsnis nėra apie tai, kuris yra „geresnis”. Tokių straipsnių internete pilna, ir dauguma jų yra arba per daug šališki, arba per daug abstraktūs. Čia bandysime išsiaiškinti, kas iš tikrųjų svarbu praktikoje – kai reikia priimti sprendimą realiam projektui, su realiais terminais ir realia komanda.

REST – senasis geras draugas

REST (Representational State Transfer) nėra protokolas ar standartas griežtąja prasme – tai architektūrinis stilius, kurį 2000 metais aprašė Roy Fielding savo disertacijoje. Idėja paprasta: resursai turi URL adresus, o HTTP metodai (GET, POST, PUT, DELETE) apibrėžia, ką su tais resursais darai.

Pavyzdžiui, jei nori gauti vartotojo informaciją, kreipies į /users/42. Jei nori jo užsakymų – /users/42/orders. Jei nori konkretaus užsakymo – /users/42/orders/7. Viskas logiška, hierarchiška, suprantama.

REST populiarumas kilo ne be priežasties. Jis puikiai dera su HTTP protokolu, kurį jau visi suprantame. Kešavimas veikia natūraliai – HTTP kešavimo mechanizmai tiesiog veikia. Debugging’as paprastas: gali atidaryti naršyklę, įvesti URL ir pamatyti atsakymą. Nereikia jokių specialių įrankių.

Tačiau REST turi vieną fundamentalią problemą, kuri laikui bėgant tapo vis labiau juntama: jis buvo sukurtas su tam tikromis prielaidomis apie tai, kaip atrodo duomenų struktūros ir kaip klientai jas naudoja. Ir šios prielaidos ne visada atitinka realybę.

GraphQL – Facebook’o atsakymas į skausmą

GraphQL atsirado Facebook’e apie 2012 metus, kai jie bandė spręsti labai konkretų problemą: jų mobilioji aplikacija darė per daug užklausų ir gaudavo per daug nereikalingų duomenų. 2015 metais technologija buvo atvirojo kodo bendruomenei.

Pagrindinė idėja: vietoj to, kad serveris diktuotų, kokius duomenis grąžina, klientas pats nurodo, ko jam reikia. Viskas vyksta per vieną endpoint’ą (/graphql), o užklausa atrodo maždaug taip:


query {
  user(id: "42") {
    name
    email
    orders {
      id
      total
      status
    }
  }
}

Serveris grąžina lygiai tai, ko paprašei – ne daugiau, ne mažiau. Tai sprendžia dvi klasikines REST problemas: over-fetching (kai gauni daugiau duomenų nei reikia) ir under-fetching (kai vienos užklausos neužtenka ir reikia daryti kelias).

GraphQL taip pat turi stiprią tipų sistemą. Schema apibrėžia, kokie duomenys egzistuoja ir kaip jie susiję. Tai reiškia, kad IDE gali pasiūlyti autocomplete’ą, o klaidos aptinkamos anksčiau – dar prieš paleidžiant kodą.

Over-fetching ir N+1 – kur slypi tikri iššūkiai

Kalbant apie REST trūkumus, dažniausiai minimas over-fetching. Jei endpoint’as /users/42 grąžina 30 laukų, bet tau reikia tik vardo ir el. pašto – gauni 30 laukų. Mobiliems įrenginiams, kur duomenų perdavimas kainuoja, tai gali būti reali problema.

Bet GraphQL turi savų galvos skausmų. Vienas iš labiausiai žinomų – N+1 problema. Įsivaizduok: prašai sąrašo 100 vartotojų ir kiekvieno jų užsakymų. Naivus GraphQL implementavimas padarys 1 užklausą vartotojams ir po 1 užklausą kiekvieno vartotojo užsakymams – iš viso 101 duomenų bazės užklausa. REST atveju tai paprastai sprendžiama JOIN’ais arba atskiru endpoint’u, kuris grąžina viską iš karto.

Sprendimas GraphQL pasaulyje – DataLoader pattern’as, kuris grupuoja užklausas. Bet tai reiškia papildomą kompleksiškumą, kurį reikia suprojektuoti ir palaikyti. Niekas neduoda nemokamai.

Kešavimas taip pat tampa sudėtingesnis su GraphQL. HTTP kešavimas, kuris REST atveju veikia beveik automatiškai, GraphQL atveju reikalauja papildomų sprendimų – ar tai būtų Apollo Client kešavimas kliento pusėje, ar persisted queries serverio pusėje. Tai sprendžiama, bet reikia apie tai galvoti.

Kada rinktis REST, o kada GraphQL

Čia prasideda praktinė dalis. Teorija gražu, bet kiekvieną kartą, kai pradedi naują projektą, reikia priimti sprendimą. Štai keletas konkrečių scenarijų:

REST yra geresnis pasirinkimas, kai:

  • Kuriate paprastą CRUD API, kur duomenų struktūros aiškios ir stabilios
  • Komanda neturi GraphQL patirties ir projekto terminas trumpas
  • Kešavimas yra kritinis reikalavimas ir norite naudoti CDN be papildomų konfigūracijų
  • API bus naudojamas trečiųjų šalių, kurios tikisi standartinio REST
  • Dirbate su mikroservisais, kur kiekvienas servisas turi aiškiai apibrėžtą atsakomybę

GraphQL verta svarstyti, kai:

  • Turite sudėtingą duomenų grafą su daug ryšių tarp objektų
  • Klientai (web, mobile, trečiųjų šalių) turi labai skirtingus duomenų poreikius
  • Komanda yra pakankamai didelė ir gali sau leisti investuoti į tinkamą infrastruktūrą
  • Realaus laiko funkcionalumas (subscriptions) yra svarbus
  • Norite stiprios tipo sistemos ir geros developer experience

Vienas dalykas, kurį verta paminėti: daugelis kompanijų naudoja abu. GraphQL kaip „API gateway” prieš kelis REST mikroservisus – tai gana populiarus pattern’as. Klientas kalbasi su GraphQL sluoksniu, o tas sluoksnis orkestrina užklausas į atskirus REST servisus. Taip gaunate geriausią iš abiejų pasaulių, bet taip pat ir papildomą kompleksiškumą.

Versioning – problema, kurią dažnai pamiršta

Vienas aspektas, apie kurį retai kalbama pradedant projektą, bet kuris tampa labai svarbus vėliau – API versioning’as. Kaip keičiate API, kai jis jau naudojamas?

REST atveju tradicinis sprendimas – URL versioning: /v1/users, /v2/users. Tai paprasta ir suprantama, bet reiškia, kad palaikote kelis API variantus vienu metu. Kiti sprendimai – header’iai arba query parametrai – egzistuoja, bet nėra taip plačiai naudojami.

GraphQL pozicija čia įdomi: oficiali rekomendacija yra vengti versioning’o. Vietoj to naudojate „schema evolution” – pridedate naujus laukus, o senus pažymite kaip deprecated. Kadangi klientai prašo tik tų laukų, kurių jiems reikia, nauji laukai nesulaužo senų klientų.

Teoriškai tai skamba puikiai. Praktikoje tai reiškia, kad schema laikui bėgant gali tapti gana „apaugusi” deprecated laukais, ir kažkuriuo momentu vis tiek reikia juos pašalinti, o tai reiškia breaking change. Taigi problema ne išnyksta, o tik atidedama.

Praktinis patarimas: nesvarbu, kurį pasirinksite, iš anksto pagalvokite apie versioning strategiją. Dokumentuokite, kada ir kaip ketinate daryti breaking changes. Klientai, naudojantys jūsų API, jums už tai bus dėkingi.

Developer Experience – tai irgi svarbu

Techninis sprendimas niekada nėra tik techninis. Žmonės, kurie dirbs su API kasdien, turi jį suprasti ir mėgti naudoti. Ir čia GraphQL turi aiškų pranašumą.

GraphiQL ir Apollo Studio – tai interaktyvios IDE, kurios leidžia tyrinėti API schemą, rašyti užklausas su autocomplete’u ir iš karto matyti rezultatus. Naujas kūrėjas, atėjęs į projektą, gali per kelias minutes suprasti, kokie duomenys egzistuoja ir kaip jie susiję. REST atveju tai paprastai reiškia Swagger/OpenAPI dokumentaciją, kuri dažnai yra pasenusi arba neišsami.

Kita vertus, REST užklausas galite testuoti tiesiog naršyklėje arba su curl. GraphQL tam reikia arba specialių įrankių, arba POST užklausų su JSON body. Tai mažas, bet realus friction’as.

TypeScript integracija su GraphQL yra tikrai gera. Įrankiai kaip GraphQL Code Generator automatiškai generuoja TypeScript tipus iš schemos. Tai reiškia, kad jei schema pasikeičia, TypeScript kompiliatorius pasakys, kur reikia atnaujinti kodą. REST atveju tai galima pasiekti su OpenAPI generatoriais, bet paprastai reikia daugiau rankinio darbo.

Kai teorija susitinka su realybe

Po viso šio aptarimo galima padaryti vieną svarbią pastabą: dauguma diskusijų apie REST vs GraphQL vyksta abstrakčiame lygyje, ignoruojant kontekstą. O kontekstas yra viskas.

Jei esate startuolis su trimis kūrėjais ir reikia greitai išleisti produktą – REST greičiausiai yra protingesnis pasirinkimas. Mažiau konfigūracijos, mažiau konceptų mokytis, daugiau laiko tikrajai logikai. GraphQL ekosistema yra galinga, bet ji turi krivę, kurią reikia įveikti.

Jei esate didelė kompanija su sudėtinga duomenų struktūra ir keliais klientais – GraphQL investicija gali atsipirkti. Bet net ir tada verta pradėti nuo paprastumo ir komplikuoti tik tada, kai yra konkreti priežastis.

Vienas dalykas, kurį tikrai rekomenduočiau: nesirinkite technologijos vien dėl to, kad ji madinga arba kad ją naudoja Netflix. Rinkitės pagal savo problemos pobūdį, komandos patirtį ir projekto reikalavimus. REST nėra pasenęs, o GraphQL nėra magija. Abu yra įrankiai, ir geras kūrėjas žino, kada naudoti kurį.

Galiausiai, nesvarbu ką pasirinksite – dokumentuokite savo sprendimą. Užrašykite, kodėl pasirinkote REST arba GraphQL, kokie buvo alternatyvos ir kokie buvo kompromisai. Tai padės ateityje, kai reikės peržiūrėti sprendimą arba kai į komandą ateis naujas žmogus ir paklaus „kodėl čia taip?”.