Hlavní navigace

Tři nástrahy GraphQL: na co si dát pozor

Martin Zlámal

Ještě před rokem jen málo firem používalo GraphQL na produkci. Situace se ale změnila a většina programátorů dnes chápe, proč je GraphQL užitečné. Otázkou však zůstává, jak ho správně používat a jaká jsou úskalí.

Doba čtení: 7 minut

Na začátku tohoto roku jsem se rozepsal o známých nástrahách GraphQL a myslím, že je vhodný čas ohlédnout se zpět a podívat se na současný stav. Tehdy jsem tvrdil, že největší úskalí GraphQL je to, že je velmi těžké ho začít používat na produkci správně a také proč je response cache je komplikovaná.

Od doby, kdy jsem psal svůj text poprvé, se však hodně věcí se změnilo. Tvrdil jsem například, že je jen málo firem, které GraphQL používají v produkci. To už dávno není pravda. Většina programátorů už chápe, proč je GraphQL užitečné. Na pohovorech, meetupech i jen tak u piva potkáváme spoustu lidí a prakticky každý má svůj projekt s GraphQL. Ne všichni ale do GraphQL šlapou naplno, protože ne všichni GraphQL skutečně potřebují.

GraphQL je dotazovací jazyk pro moderní API. Díky GraphQL je možné deklarativně definovat požadavky aplikace na data, což zcela změnilo způsob, jak dnes píšeme React aplikace. GraphQL je navíc striktně typované API s bohatým ekosystémem nástrojů, které posouvají kvalitu FE aplikací na novou úroveň.

My používáme GraphQL v Kiwi.com prakticky od začátku pražské pobočky – od vyhledávání letů přes FAQ a vyhledávání lokací až po mobilní zařízení. Dává to smysl, protože pražská pobočka byla ve svých začátcích hodně JS-centrická. Otázkou už proto dávno není „proč GraphQL”, ale „jak”.

Vstupní bariéra je stále velká

GraphQL je kompletně jiný svět v porovnání s předchozím stylem vývoje a to má zásadní dopad na způsob, jakým píšeme dnešní aplikace. Například za poslední rok se z našich kódů téměř vytratil Redux, na což mělo velký vliv právě GraphQL (a React Context). Všechny tyto zásadní změny bylo možné dělat díky tomu, že stále máme GraphQL pod palcem: využíváme proxy, za kterou schováváme všechny zvláštnosti našich REST API.

Tato proxy byla vytvořena před rokem a půl a motivací bylo získat čas, než GraphQL začne být zajímavé pro celou firmu (zejména klientské části aplikace). Důležité je, že tato proxy byla vytvořena JS programátory, tedy přímými konzumenty tohoto API.

To se ukázalo jako klíč k dobrému návrhu API pro JS klienty. Dostatečně zkušený React programátor navrhne tento graf perfektně pro daný use-case, protože ví, jak bude data potřeba zobrazit a jak se skládají React componenty. Osobně považuji GraphQL za rozšíření frontendu: občas do GraphQL schováváme věci, které jsou jinak v JS klientovi. Tím se celý JS klient násobně zjednoduší a navíc je možné tyto části bez přemýšlení znovu použít pro jinou aplikaci.

Jedním z příkladů je zobrazování bookingů našich letů: v GraphQL připravíme minulé a budoucí lety, připravíme, co znamená jednosměrná a return letenka (a mnoho další logiky) a JS klient vše prostě jen vykreslí (bez dalších if/else). Navíc fragmenty dat sedí na React komponenty jako „prdel na hrnec”.

Nicméně v současné chvíli jsme v pozici, kdy je třeba postoupit do dalšího stádia vývoje a GraphQL začínají vytvářet spíše Python vývojáři. A výsledné grafy dat vypadají často následně:

Původní REST API zde mělo zřetelný dopad na výsledný návrh. React komponenty vytvářejí stromovou strukturu, a proto je velmi užitečné, když i data mají stromovou strukturu (to však neznamená, že DB struktura = GraphQL struktura!). To je rozdíl FE a BE devs mindsetu. BE potřebuje dostat data z databáze ven. FE potřebuje udělat z dat perfektní UI a taková struktura dat to komplikuje. Tiše si říkáme „alespoň že máme GraphQL”.

Naším cílem je dostat se do stavu tzv. “GraphQL Native”, protože proxy starých API nelze udržovat navždy. To je však naše současná (a pravděpodobně největší) bariéra. Kiwi.com je v pozici, kdy máme více Git repozitářů než programátorů. Nick Schrock, jeden z tvůrců GraphQL, popsal tento stav jako „insanity at large” a je moc dobře cítit, že málokdo tuší, jak postavit GraphQL Native dobře (tedy bez potřeby GraphQL proxy). Mrkněte na následující video, kde se na toto téma Nick Schrock rozohnil:

Zdá se, že současný trend je řešit tento problém pomocí schema-stitchingu. Spíše než řešení je to přikrytí celého problému dekou. Jak se s tímto problémem vypořádáme, pravděpodobně uvidíme zhruba za rok.

GraphQL už je ready pro produkci

GraphQL je stále nevhodné použít tak, jak je. Podobně jako React je třeba i GraphQL napřed trošku hýčkat, než jej bude možné skutečně nasadit. Ukázalo se však, že je zapotřebí věnovat se jiným (důležitějším) problémům, než je server-side rendering (SSR) a persistent queries. V první řadě SSR už dávno není problém. Řešení je dlouho známé a funkční a persistent queries se dají vyřešit quick-and-dirty tak, jako do dělá například Apollo.

Tento přístup však nepoužíváme protože to zaprvé nejsou skutečné “persisted queries” ale spíše “ephemeral queries” a hlavně již nepoužíváme Apollo Engine.

Na začátku roku jsem ještě Apollo Engine (AE) vychvaloval. Postupem času se však ukázalo, že tato služba není až tak užitečná a že je plná chyb. Nejdůležitějším prvkem pro nás byl pravděpodobně error reporting a ten dost často vůbec nefungoval, nebo byla výstupem informace typu “je tam chyba” – což je žalostně málo.

Nebylo nám úplně jasné, jestli chceme AE kvůli těmto (a mnoha dalším) nedostatkům používat, a tak se nám nechtělo investovat do placeného programu. Toto dilema se vyřešilo rychle: náš traffic jednoho dne vzrostl násobně víc, než bylo povoleno, a AE nám dal bez upozornění ban. Dnes proto pro tento monitoring používáme ELK jako náhradu za AE a věci si děláme tak, jak potřebujeme, sami v Kibaně. Navíc se ukázalo, že AE byl velmi náročný na potřebný výkon serverů a paměti – po odstranění AE spadla paměť na desetinu. Výhra pro nás.

Toto pěkně vystihuje trend, který jsem pozoroval po celý rok. V lednu jsem se ptal: Are there any other available (AE) options? ¯\_()_/¯

Řešením bylo “DIY”. Vyplatí investovat svůj čas do nástrojů a vzdělání. Vzdělání platí u jakékoliv technologie, ale nástroje hrají u GraphQL klíčovou roli. To uvidíme zejména u přechodu na GraphQL Native.

Response cache je komplikovaná

Cache je komplikovaná vždy a pro cache GraphQL odpovědí to platí obzvlášť. V GraphQL nelze vzít odpověď ze serveru a uložit ji naivně někam do cache. GraphQL odpověď je nutné rozebrat na jednotlivé typy a ukládat je podle jejich unikátních ID. Nebezpečné na této nástraze je, že si to spousta lidí neuvědomuje. Problém je totiž v tom, že jednotlivé části dat mohou přijít z různých zdrojů (různých query nebo mutací). Tedy i zdánlivě nesouvisející dotaz na server může mít dopad na cache nějakého jiného objektu. V původním článku jsem tento fakt ukazoval na následujícím příkladu:

{
 allBookings {
   id, status # , ...
 }
}

Tato query vrátí všechny naše bookingy a s nimi související data. Druhá nesouvisející query si později vyžádá jeden booking:

{
 booking(id: "0paqu3==") {
   id, status # , ...
 }
}

Je zřejmé, že tato druhá odpověď ovlivní cache prvního požadavku (allBookings), a proto je třeba přistupovat ke cachování odpovědí trošku opatrněji. Naše strategie se za poslední rok trošku změnila. Stále používáme naivní cache a ukládáme odpověď na GraphQL dotaz 1:1. Tato cache však slouží spíše jako burst cache odchozích requestů. JS klienti (web, React Native) zkrátka neodešlou druhý identický request v krátkém časovém intervalu. Mobilní klienti však potřebují mít offline přístup ke všemu, co je k dispozici, a k tomuto účelu používáme přímo Relay store a v případě React Native AsyncStorage. Když je zařízení offline, tak čteme z lokálního úložiště a pokud není, tak posíláme normální požadavek na server a normalizovaný výsledek se ukládá na disk (o to se postará Relay).

Osobně se držím zpět ve vylepšování této části, protože v Relay se potichu připravuje nový “QueryRenderer”, který toto chování bude umět sám od sebe. V této nové verzi bude možné ovlivnit read a fetch policy. To vyřeší většinu běžných požadavků na query renderer.

Investujte do svého GraphQL

Skvělé doporučení, které jsem dostal na GraphQL Europe konferenci. Chce to nějaký čas porozumět všem aspektům tohoto API a stejně tak je třeba čas na prošlapání té ideální cesty. Mé doporučení je navíc sledovat, co se děje přímo u tvůrců GraphQL a Reactu. Všichni začínají více a více tlačit na změny na serveru, protože použití GraphQL na klientech už je víceméně vyřešený problém. Skvělá ukázka GraphQL Native je nedávné odstranění deferrable queries z Relay. V Relay šlo velmi jednoduše odložit fetch drahých fragmentů na později a to bez změny na serveru. To už však přestává být potřeba na tato zodpovědnost se pomalu přesouvá na server. Nebylo jednoduché udělat to rovnou, protože deferred queries jsou komplikovaný problém. Nicméně GraphQL Native je jasná cílová destinace a tyto změny jsou nyní potřeba.

Jsem zvědavý, jaký bude další level v této oblasti. Bohužel se zdá, že většina firem přehnaně zareagovala na potřebu microservices, jak popisoval Nick Schrock v předchozím videu, a to ovlivní návrh celého ekosystému. V současné chvíli je jediné známé a vyzkoušené řešení “GraphQL Native” schema stitching. Máme nápady, jak to vyřešit lépe, ale bez investice do GraphQL to nepůjde. Investujte do svého GraphQL!

Našli jste v článku chybu?