GraphQL feloldók: Legjobb gyakorlatok

A graphql.org oldalról

Ez a bejegyzés a bevált gyakorlatok és megfigyelések sorozatának első része, amelyeket a PayPalnál a GraphQL API felépítése során tettünk. A következő hozzászólásokban megosztjuk gondolataikat a következőkről: sématervezés, hibakezelés, a termelés láthatósága, az ügyféloldali integrációk optimalizálása és a csapatok eszközöi.

Lehet, hogy látta korábbi „GraphQL: A PayPal Checkout sikertörténete” című bejegyzésünket a PayPal REST és a GraphQL közötti utazásáról. Ez a bejegyzés részletekbe menti a bevált gyakorlatokat a megoldók felépítéséhez, amelyek gyorsak, tesztelhetőek és rugalmasak az idő múlásával.

Mi a megoldó?

Kezdjük ugyanabban az alapvonalon. Mi a megoldó?

A felbontó meghatározása
Minden típust minden mezőt egy feloldónak nevezett függvény támogat.

A feloldó egy olyan funkció, amely egy séma egyik típusának vagy mezőjének értékét feloldja. A felbontók objektumokat vagy skálákat, például karakterláncokat, számokat, logikai értékeket stb. Adhatnak vissza. Ha egy objektum visszaadásra kerül, a végrehajtás a következő gyermekmezőre folytatódik. Ha egy skalár visszatér (általában egy levélcsomóponton), akkor a végrehajtás befejeződik. Ha a null érték visszatér, a végrehajtás megáll, és nem folytatódik.

A felbontók aszinkronak is lehetnek! Meg tudják oldani az értékeket egy másik REST API-ból, adatbázisból, gyorsítótárból, állandóból stb.

Később áttekintünk egy sor példát, amelyek bemutatják, hogyan lehet gyors, tesztelhető és rugalmas megoldókat felépíteni.

Lekérdezések végrehajtása

A megoldók jobb megértése érdekében tudnia kell, hogy miként hajtják végre a lekérdezéseket.

Minden GraphQL lekérdezés három fázison megy keresztül. A lekérdezések elemzése, érvényesítése és végrehajtása megtörténik.

  1. Elemzés - A lekérdezés egy absztrakt szintaxis fába (vagy AST) elemzésre kerül. Az AST hihetetlenül nagy teljesítményű és olyan eszközök mögött található, mint az ESLint, babel, stb. Ha szeretné látni, hogy néz ki egy GraphQL AST, nézd meg az astexplorer.net webhelyet, és váltsd a JavaScriptet GraphQL-re. Bal oldalon egy lekérdezés, jobb oldalon AST jelenik meg.
  2. Érvényesítés - Az AST érvényesül a séma alapján. Ellenőrzi a helyes lekérdezési szintaxist és a mezők létezését.
  3. Végrehajtás - A futási idő áthalad az AST-n, a fa gyökerétől kezdve, felhívja a megoldókat, összegyűjti az eredményeket és JSON-t bocsát ki.

Ebben a példában a következő lekérdezésre utalunk:

Lekérdezés későbbi referencia céljából

Amikor ezt a lekérdezést értelmezik, az AST-ként vagy fává alakul.

A lekérdezés faként jelenik meg

A gyökérlekérdezés típusa a fa belépési pontja, és tartalmazza a két gyökérmezőünket, a felhasználót és az albumot. A felhasználó és az album feloldóját párhuzamosan hajtjuk végre (ami jellemző az összes futási időre). A fát az első szélességgel hajtják végre, azaz a felhasználót meg kell oldani, mielőtt a gyermeke neve és e-mail címe végrehajtásra kerül. Ha a felhasználó feloldója aszinkron, akkor a felhasználói ág késlelteti a megoldását. Miután az összes levélcsomópont, a név, az e-mail, a cím meg lett oldva, a végrehajtás befejeződött.

A Gyökérlekérdezés mezőket, például a felhasználót és az albumot, párhuzamosan hajtják végre, de nem külön sorrendben. A mezőket általában abban a sorrendben hajtják végre, ahogyan megjelennek a lekérdezésben, de ezt nem biztonságos feltételezni. Mivel a mezőket párhuzamosan hajtják végre, feltételezzük, hogy atomi, idempotensek és mellékhatásoktól mentesek.

Közelebbről nézve a felbontókat

A következő néhány szakaszban a JavaScriptet fogjuk használni, de a GraphQL szerverek szinte bármilyen nyelven írhatók.

Négy érvvel feloldható megoldás - gyökér, argumentumok, kontextus, információ

Bizonyos formában, minden nyelven minden megoldó megkapja ezt a négy érvet:

  • root - az előző / szülő típushoz tartozó eredmény
  • args - A mezőhöz nyújtott érvek
  • kontextus - módosítható objektum, amelyet minden feloldó rendelkezésére bocsátanak
  • info - a lekérdezéshez kapcsolódó mezőspecifikus információk (ritkán használják)

Ez a négy érv alapvető fontosságú annak megértésében, hogy az adatok hogyan folynak a felbontók között.

Alapértelmezett megoldók

Mielőtt folytatnánk, érdemes megjegyezni, hogy a GraphQL szerver beépített alapértelmezett felbontóval rendelkezik, tehát nem kell minden mezőhöz megadnia a felbontó funkciót. Az alapértelmezett megoldó a gyökérzetben fog keresni, hogy a mezővel azonos nevű tulajdonságot találjon. A megvalósítás valószínűleg így néz ki:

Alapértelmezett megoldó implementáció

Adatok beolvasása a felbontóban

Hol kell adatokat letölteni? Melyek a kompromisszumok lehetőségeinkkel?

A következő néhány példában visszatérünk erre a sémára:

Az eseménymezőnek meg kell adnia az id argumentumot, visszaad egy eseményt

Adatok átadása a felbontók között

A kontextus egy változtatható objektum, amelyet az összes feloldó biztosítja. Minden kérés között létrehozták és megsemmisítették. Ez egy remek hely a közös Auth-adatok, az API-k és az adatbázisok általános modelljeinek / lekérdezőinek stb. Tárolására. A PayPalban egy nagy Node.js üzlet vagyunk, amely az Expressre épül, vagyis az Express követelményeit ott tároljuk.

Amikor először megismerik a kontextust, először gondolhatnánk, hogy a kontextust használjuk általános célú gyorsítótárként. Ez nem ajánlott, de itt lehet, hogyan néz ki egy megvalósítás.

Adatok továbbítása a felbontók között a kontextust használva. Ez nem ajánlott!

A cím meghívásakor az esemény eredményét kontextusban tároljuk. Amikor a photoUrl meghívásra kerül, az eseményeket kiszűrjük a környezetből és felhasználjuk. Ez a kód nem megbízható. Nem garantáljuk, hogy a címet a photoUrl előtt végrehajtják.

Meg tudnánk javítani mindkét megoldót, hogy ellenőrizzük, van-e esemény az összefüggésben. Ha igen, akkor használja. Ellenkező esetben lehívjuk és később tároljuk, de még mindig van nagy felület a hibák elkerülésére.

Ehelyett el kell kerülnünk a kontextus mutációját a felbontókban. Meg kell akadályoznunk, hogy a tudás és az aggodalmak összekeveredjenek egymással, hogy megoldóink könnyen megértsék, hibakereshetők és tesztelhetők legyenek.

Adatok átadása a szülőktől a gyermekekig

A gyökér érv az adatok továbbítása a szülői feloldókról a gyermekfeloldókra.

Például, ha olyan eseménytípust épít, ahol az esemény összes mezője található
ugyanazon adatoktól függ, érdemes egyszer lekérni azokat az esemény mezőbe,
nem pedig az esemény minden területén.

Jó ötletnek tűnik, igaz? Ez egy gyors módja annak, hogy megkezdje a megoldók felépítését, de előfordulhat, hogy problémák merülnek fel. Megértjük miért.

Az alábbi példákkal együtt olyan eseménytípussal fogunk dolgozni, amelynek két mezője van.

Eseménytípus két mezővel: cím és photoUrl

Az esemény mezőinek többségét egy esemény API-jából lehet letölteni, így a legfelső szintű eseményfeloldóban lehet letölteni, és az eredményeket megadhatjuk a cím és a photoUrl megoldóknak.

A legfelső szintű eseménymegoldó adatokat tölt le, eredményeket biztosít a cím és a photoUrl mezőfeloldók számára

Még jobb, ha nem kell megadnunk az alsó két megoldót.
Az alapértelmezett felbontókat használhatjuk, mert az objektum a getEvent () által visszaadott
rendelkezik egy címmel és egy photoUrl tulajdonsággal.

Az azonosító és a cím az alapértelmezett feloldókkal oldódik meg

Mi a baj ezzel?

Két forgatókönyv létezik, ha túllépi a letöltést ...

1. forgatókönyv: Többrétegű adatok letöltése

Tegyük fel, hogy bizonyos követelmények teljesülnek, és meg kell jelenítenie az esemény résztvevőit. Először a résztvevők mezőjét adjuk hozzá az eseményhez.

Esemény típusa további résztvevő mezővel

A résztvevők adatainak lekérésekor két lehetőség áll rendelkezésére: az adatok letöltése az eseménymegoldóban, vagy a résztvevők feloldása.

Kipróbáljuk az első lehetőséget: hozzáadjuk az eseménymegoldóhoz.

Az eseménymegoldó két API-t hív meg, beolvasva az esemény részleteit és a résztvevők részleteit

Ha az ügyfél csak a címet és a photoUrl-t kérdezi, de nem jelenik meg a résztvevők.Most nem vagy hatékony, és felesleges kérést tesz a résztvevők API-jához.

Ez nem a te hibád, így működünk. Felismerjük a mintákat és lemásoljuk őket.
Ha a közreműködők látják, hogy az adatok lekérése az eseménymegoldásban zajlik, akkor valószínű
adjon hozzá további adatokhoz, anélkül, hogy túl erősen gondolkodna rajta.

Van még egy lehetőség, hogy teszteljük a résztvevők letöltését a résztvevők feloldójában.

a résztvevők feloldója lehívja a résztvevők adatait az Attendees API-ból

Ha ügyfelünk csak a résztvevőkről kér, a cím és a photoUrl nem. Még mindig hatástalanok vagyunk, ha felesleges kérést teszünk az események API-jához.

2. forgatókönyv: N + 1 probléma

Mivel az adatok beolvasása terepi szinten történik, fennáll annak a kockázata, hogy túlságosan lekérjük. A túltöltés és az N + 1 probléma népszerű téma a GraphQL világban. A Shopify nagyszerű cikke ismerteti az N + 1-et.

Hogyan befolyásol minket itt?

A jobb szemléltetés érdekében hozzáadunk egy új eseménymezőt, amely visszaadja az összes eseményt.

Az esemény mező minden eseményt visszaad.Az összes esemény lekérdezése a címmel és a résztvevőkkel

Ha az ügyfél minden eseményt és a résztvevőket kérdezi, akkor fennáll a túl letöltés veszélye, mivel a résztvevők egynél több eseményen is részt vehetnek. Lehet, hogy másolatot kérünk ugyanazon résztvevő számára.

Ez a probléma egy olyan nagy szervezetben felerősödik, ahol a kérelmek elszivároghatnak, és szükségtelen nyomást gyakorolhatnak a rendszerére.

Ennek megoldásához be kell vonnunk és le kell vonnunk a kérelmeket!

A JavaScript-ben néhány népszerű lehetőség a dataloader és az Apollo adatforrások.

Ha másik nyelvet használ, akkor valószínűleg van valami, amit felvethet. Tehát nézz körül, mielőtt ezt egyedül megoldja.

Alapjában ezek a könyvtárak az adathozzáférési réteg tetején helyezkednek el, és gyorsítótárazzák és eltávolítják a kimenő kérelmeket, lebontás vagy memoálás segítségével. Ha kíváncsi, hogy néz ki az aszinkron emlékezet, nézd meg Daniel Brain kiváló üzenetét!

Adatok lekérése terepi szinten

Korábban láttuk, hogy könnyű megégni, ha túlszűrik a „legnehezebb” szülő-gyermek megoldókkal.

Van-e jobb alternatíva?

Utánozzuk újra a szülő-gyermek lehetőséget. Mi lenne, ha ezt megfordítanánk úgy, hogy a gyermekmezők felelősek a saját adatainak beolvasásáért?

A mezők felelősek a saját adataik letöltéséért.
Miért jobb alternatíva?

Ezt a kódot könnyű megérteni. Pontosan tudja, hová tölt be egy e-mailt. Ez megkönnyíti a hibakeresést.

Ez a kód tesztelhetőbb. Nem kell kipróbálnia az eseménymegoldót, amikor tényleg csak a címmegoldót akarta kipróbálni.

Néhánynak a getEvent másolat kódszagnak tűnhet. De ha egyszerű, könnyen megfontolható és ellenőrizhetőbb kóddal rendelkezik, érdemes kicsit megismételni.

De még mindig fennáll egy lehetséges probléma. Ha az ügyfél címet és photoUrl-t kér, akkor további eseményt kérünk az Event API-n a getEvent használatával. Amint azt korábban láttuk az N + 1 problémában, a keretek szintjén meg kell hamisítanunk a kéréseket olyan könyvtárakkal, mint a dataloader és az Apollo adatforrások.

Ha mezőszintű adatokat töltünk le és levonjuk a kéréseket, akkor van olyan kód, amelyet könnyebb hibakeresni és tesztelni, és az adatok optimális lekérése nélkül gondolkodhatunk.

Legjobb gyakorlatok

  • Az adatok szülőtől-gyermekéhez történő letöltését és átadását óvatosan kell felhasználni.
  • Használjon olyan könyvtárakat, mint a dataloader, a későbbi kérelmek eloszlatására.
  • Légy tudatában az adatforrásokra gyakorolt ​​bármilyen nyomásnak.
  • Ne módosítsa a „kontextust”. Biztosítja a következetes, kevésbé hibás kódot.
  • Írjon olvasható, fenntartható, ellenőrizhető megoldókat. Nem túl okos.
  • A lehető legvékonyabbá tegye a feloldókat. Kicsomagolja az adatok lekérésének logikáját az újrafelhasználható aszinkron függvényekhez.

Maradjon velünk!

Gondolatok? Szeretnénk hallani a csapata legjobb gyakorlatait és tapasztalatait az épületmegoldókkal kapcsolatban. Ez egy olyan téma, amelyet gyakran nem tárgyalnak, de fontos a hosszú életű GraphQL API-k létrehozásához.

A következő hozzászólásokban megosztjuk gondolataikat a következőkről: sématervezés, hibakezelés, a termelés láthatósága, az ügyféloldali integrációk optimalizálása és a csapatok eszközöi.

Mi felveszünk! Ha a front-end infrastruktúrán szeretne dolgozni, a GraphQL-n vagy a React-en a PayPalnál, DM nekem a Twitteren, a @mark_stuart-on!