Qest
Published in

Qest

Jak vytvořit GraphQL federaci v NX? Přehledný návod, jak na to

V tomto článku si ukážeme, jakým způsobem můžete vytvořit Federation gateway za pomocí monorepa v NX. Zaměříme se na jednotlivé kroky, možnosti řešení i nejčastější chyby, které vás při tvorbě mohou potkat.

Co je monorepo a Apollo Federation Gateway?

Monorepo je speciální případ repozitáře, při kterém se vyvíjí více nezávislých projektů a jejich kód se uchovává v rámci jednoho repozitáře.

Apollo Federation Gateway je architektura, která kombinuje několik rozhraní GraphQL API. S federací můžeme zodpovědně sdílet vlastnictví supergrafů napříč libovolným libovolným počtem týmů.

Jak to celé funguje?

Ve federované architektuře se jednotlivá rozhraní GraphQL API nazývají subgrafy. Z nichž se vytváří tzv. supergrafy, které používá právě gateway. Pomocí dotazů na vytvořený supergraf se mohou klienti ptát na všechny subgrafy najednou. Brána tedy slouží jako přístupový bod pro náš supergraf. Klient se může dotazovat na jakýkoliv GraphQL server, aniž by musel něco konfigurovat.

Příklad z praxe
Nyní si ukážeme příklad, jak to celé v reálu může vypadat. Představme si, že máme dvě GraphQL aplikace, které nám běží na různých portech. První bude spravovat uživatele, pojmenujeme si jí userapp, a druhá články, tu si pojmenujeme articleapp. Tyto aplikace spolu mohou navzájem komunikovat. Dále bychom měli FE aplikaci (na obrázku client), která nám představuje knihovnu. Knihovna si spravuje uživatele a články. Musí být tedy napojena na naše dvě GraphQL aplikace. To nám zajistí právě federation gateway.

Tvorba gateway
Vytvoření gateway se dá řešit dvěma způsoby. Prvním způsobem je vytvořit gateway, která má v sobě vložený objekt s položkou serviseList. Uvnitř se jen nadefinuje název aplikace a url, nad kterou aplikace poslouchá.

https://gist.github.com/mikesjcz/35bb6abcfb2418eb292fe1e9a5ca454a

Toto řešení má však jednu velkou nevýhodu. Pokud nám gateway poběží a nějaká z aplikací spadne, přestane fungovat jen postižená část aplikace. Když se aplikaci podaří opravit, okamžitě po nasazení nám začne v bráně fungovat. Problém ale nastává tehdy, pokud by došlo k výpadku samotné gateway. Gateway se začne restartovat a pokusí se z aplikací stáhnout schémata (ukázka v kódu).

Problém také nastane, když nějaká z aplikací neběží. Může se stát, že se některou aplikaci nepodaří nastartovat, a je třeba ji ručně nahodit. Federation gateway se pokusí stáhnout schémata, to se jí nepodaří, protože všechny aplikace neběží, a aplikace spadne. V tu chvíli nám federation gateway vůbec neběží, a to je velký problém. My však potřebujeme, aby aplikace běžela i bez toho, aniž by byly všechny aplikace v běhu. Z tohoto důvodu je dobré použít pro implementaci druhý zmiňovaný způsob.

Použití supergrafu
Druhým způsobem je použití supergrafu, který obsahuje veškerá schémata aplikací, nad kterými brána běží a na jaké url poslouchají. Apollo Federation Gateway předá objekt, který obsahuje položku supergraphSdl, a ta pak přidá obsah supergrafu ve stringové podobě.

https://gist.github.com/mikesjcz/874ae7ff6fc109ebfecc39f548c019f7

Aplikace si při spuštění stáhne ze souboru obsah supergrafu, čímž gateway získá veškerá schémata aplikací a url adresy, které ke spuštění potřebuje, a už se dále nestará, zda aplikace skutečně běží či ne.

Postup při tvorbě Apollo Federation gateway

Nejprve jsem si vytvořil za pomocí NX workspace pro monorepo, následně jsem vytvořil dvě GraphQL aplikace a nad nimi Federation gateway, která je schopna načítat supergraph a následně si kontrolovat, zda je aktuální. Pokud zjistí, že je obsah rozdílný, než je obsah nasazeného supergrafu, server se zastaví, gateway si načte aktuální obsah a server se restartuje. K přenasazování supergrafu bylo třeba vytvořit i nějaký mechanismus, který by vytvářel nové supergrafy. K tomu jsem vytvořil separátní aplikaci, která nám supergrafy vytváří a kontroluje, zda není dostupný nový oproti aktuálnímu ve zvoleném úložišti.

Co se úložišť týče, existuje mnoho možností, kam supergrafy ukládat. Já si pro ukládání zvolil dvě místa. Jedním je lokální místo na disku a druhým je databáze. Více si k tomu však řekneme níže, kde si celý proces popíšeme.

Odkaz na celý workspace je možné stáhnout zde: https://github.com/qest-cz/gql-federtion-sample

Jak vytvořit monorepo, aplikace a knihovny

První věcí, kterou musíme udělat, je nainstalovat NX (NPM i NX) a vytvořit workspace pro naše monorepo. Zadáme do konzole tento příkaz.

npx create-nx-workspace@latest monorepo-ws

Nyní se nám vytvořil work-space s názvem monorepo-ws. V adresářové struktuře se nám vytvořily dva hlavní adresáře, a tím jsou apps, kam NX ukládá aplikace, a libs, do kterých ukládá vlastní vytvořené knihovny.

Nyní si ukážeme, jak naprogramovat federation gateway tak, aby nám odpovídala popisu ukázce v horní části článku.

Nejprve si vytvoříme dvě aplikace představující uživatele a články. Pro spuštění jednotlivých serverů aplikací jsem vytvořil knihovnu, která se o spuštění sama postará. Nová knihovna se vytvoří zavoláním

nx g @nrwl/node:lib server –buildable

NX nám automaticky přidá novou knihovnu do ts.config.base.json a my už můžeme jednoduše volat její funkce. Zde je ukázka jak importovat funkci z vytvořené knihovny.

https://gist.github.com/mikesjcz/7c13f5d9c7a15d7a5ede0cf38a78839b

Nyní když máme připravené volání apollo serveru, můžeme přistoupit k vytvoření jednotlivých aplikací. Aplikace se vytváří stejně jako v případě knihoven za pomocí NX. Zavoláme tedy nx g @nrwl/node:application userapp. Aplikaci spustíme příkazem nx run userapp:serve (aplikace používá enviromens). V NX fungují tak, že si vytvoříme soubor .env.local v konkrétní aplikaci. V případě produkce bychom vytvořili .env. NX nejprve vezme data z .env.local, následně z .env (data v .env.local mají přednost). Samozřejmě je také možnost spuštění z externího zdroje.

Aplikace userapp a articleapp

Struktura obou aplikací vypadá následovně. Spouštěcím souborem je soubor main.ts, kde je zajištěno nastartování serveru s příslušnými datasources.

V adresáři resources jsou připravené moduly, které se předají serveru. Dále jsou zde definovaná GraphQL schémata aplikace a příslušné resolvery. Soubor interfaces.ts obsahuje interfaces vygenerované na základě GraphQL schématu. V hlavním adresáři monorepa je vytvořený soubor codegen.yml, kde je definované, co se má kam vygenerovat. Generování se zavolá příkazem yarn graphql-code-generator.

Dalším adresářem je data-source, do kterého jsou generovány veškeré funkcionality pro resolvery.

Posledním adresářem jsou services. Aplikace je připojená za pomocí prismy k databázi, takže pracuje s reálnými daty.

Prisma v monorepu

Napojení na databázi za pomocí prismy jsem řešil vytvořením knihoven, které mi vygenerují konkrétního klienta pro konkrétní aplikaci. Zavoláme tedy nx g @nrwl/node:lib prisma-user-app-client –buildable. Nyní uvnitř vytvoříme adresář prisma se souborem schema.prisma, kde nadefinujeme připojení k DB a vytvoření jednotlivých tabulek. Pro nastavení url databáze můžeme použít enviromens. Vytvoříme si soubor .env, kam tyto údaje můžeme zadat a následně je volat ve schématu jako env(identifikátor).

Nyní se přepneme do adresáře vytvořené knihovny a spustíme migraci, která nám vytvoří nadefinované tabulky v databázi. Zavoláme tedy npx prisma migrate dev --name init.

Teď je třeba ještě vygenerovat samotného klienta. Vrátíme se do root adresáře monorepa a zavoláme yarn prisma generate --schema=./libs/prisma-article-client/prisma/schema.prisma na naše konkrétní schéma. Nyní si jen v index.ts, vytvořené knihovny klienta, vrátíme vše z adresáře generated.

Nyní si odtud stačí klienta importovat a inicializovat.

Federation gateway

Hlavní problematikou při implementaci Apollo Federation gateway bylo, jak spustit aplikaci nezávisle na aplikacích, zda běží či ne. Řešením je použití takzvaného supergrafu.

Supergraf je GraphQL soubor, který nese schémata všech aplikací. Aplikace si tedy při spuštění stáhne aktuální schémata všech aplikací (supergraf), a ty předá apollo serveru. Když se podíváme, kde nám aplikace běží, uvidíme všechny query a mutace, které mají aplikace, nad nimiž běží gateway. Pokud nám některá aplikace běží, můžeme nad ní volat GraphQL dotazy. Samozřejmě ta, která neběží, nám bude vracet chyby. Důležité ale je, že výpadek nějaké aplikace nám nezpůsobí výpadek celé gateway, ale jen konkrétní oblasti.

Jak vytvořit supergraf

Jak již bylo řečeno výše, supergraf obsahuje schémata všech aplikací tzv. subgrafů. Nejprve tedy musíme vytvořit všechny subgrafy. Veškeré operace na generování grafů nám zajišťuje knihovna rover.

Aby bylo možné subgrafy vytvořit, musí s federací počítat i schéma aplikace. To zajistíme zavoláním funkce buildSubgraphSchema z balíčku @apollo/subgraph uvnitř inicializace apolloServeru. Nyní si ukážeme, jak subgraf vytvořit. Spustíme naší GraphQL aplikaci, v našem případě userapp, aby si rover mohl z aplikace stáhnout schéma, zavoláme příkaz

rover subgraph introspect http://localhost:8080/graphql > ./apps/federation-gateway/src/services/userapp.graphql

Provoláním se nám vytvoří userapp.graphql soubor, který nese celé schéma aplikace.

Když máme vytvořené všechny subgrafy, můžeme se pokusit vytvořit supergraf. Nejprve je nutné vytvořit konfigurační soubor, kde si nadefinujeme, z jakých schémat a na jakých url aplikace poslouchají. V mém případě konfigurační soubor vypadá takto.

Nyní máme vše připravené a nic nám nebrání k vytvoření supergrafu. Zavoláme příkaz

rover supergraph compose — config supergraph-config.yml > supergraph.graphql

který nám vytvoří supergraf. Nyní stačí stringový obsah souboru předat do ApolloGateway a tu následně předat ApolloServeru.

https://gist.github.com/mikesjcz/f4fb317f36fe563b395f2c7c88fe3b85

Úložiště pro supergrafy

Abychom mohli se supergrafy pracovat, musíme je také někam ukládat. Proto jsem prozatím zvolil dvě možnosti úložiště: a to ukládání na lokální disk a nebo do databáze. Způsob úložiště je možné vybrat z konfigurace (nastavení enviroments). Jak již bylo zmíněno v předchozí části, rover nám zajistí vytvoření souboru supergrafu. To nám zajišťuje aplikace supergraph-manager (té se však budeme věnovat v sekci automatické přenasazení).

Nyní vytvořený soubor má vždy název supergraph + časové razítko.graphql. Časovým razítkem je zajištěno, že nám nevznikne duplicitní soubor. Reálně název může vypadat třeba takto supergraph1660828030983.graphql. Ten je dočasně uložen uvnitř zmíněmé aplikace supegraph-manager. Po všech porovnání aktuálnosti se z aplikace tento soubor smaže.

Nyní k samotnému uložení. V obou případech (jak v lokálním úložišti, tak v databázi) si musíme vytvořit záznam pro supergraph, se kterým bude gateway a supergraph-manager komunikovat. V lokálním úložišti to bude soubor supergraph.graphql, v databázi zase záznam s klíčem supergraph.graphql. Tento soubor/záznam nám představuje aktuální podobu supergrafu. V případě, že se supergraph vytvořený roverem se neshoduje s předchozím, přepíše se obsah aktuálního souboru/záznamu v úložišti a s časovým razítkem se uloží jeho kopie pro případné nasazení starší verze.

Automatické přenasazení

Po spuštění serveru běží automatická časová kontrola aktuálního supergrafu. Limit je možné nastavit v konfiguraci. Pro ověření, zda nemáme nový supergraf, který bychom mohli v apollo serveru aktualizovat, jsem vytvořil knihovnu supergraph-manager. Ta zajišťuje vytažení, zápis a kontrolu aktuálního supergrafu ve zvoleném úložišti.

Knihovna si uchovává poslední nasazenou verzi v paměti. Federation gateway si zažádá o stažení posledního supergrafu za pomocí knihovny supergraph-manager. Následně si za pomocí této knihovny zkontroluje obsah s aktuálním supergrafem, který si knihovna drží v paměti. Pokud je jiný, knihovna si aktualizuje obsah a dojde k přenasazení serveru. Jednoduše zavoláme na apollo server await stop(), gateway předáme nový obsah supergrafu a server opět spustíme. Změna se projeví prakticky okamžitě.

https://gist.github.com/mikesjcz/4a510833df42d1011868ea0389a1407e

Aplikace, která aktualizuje supergraf

Abychom dokázali automatizovaně přenasazovat supergraf za běhu federation gateway, bylo třeba udělat mechanismus, který by byl schopen nezávisle na gateway kontrolovat a vytvářet nové supergrafy. Vytvořil jsem separátní aplikaci update-supergraph-app, která tuto funkčnost přesně zajišťuje.

Aplikace provádí v časovém intervalu všechny operace spojené s již zmíněnou knihovnou rover. Vytvoří si veškeré subgrafy, z nich si vytvoří supergraf. Následně použije vytvořenou knihovnu k ověření, zda došlo k nějaké změně od aktuálního, který si knihovna drží v paměti. Pokud je obsah jiný, knihovna si jej aktualizuje a pošle ho do zvoleného úložiště spolu s jeho kopií, která má časové razítko. Kopie se ukládá, pokud bychom chtěli přenasadit starší verzi supergrafu. Následně všechny dočasné soubory vytvořené roverem smaže.

Závěr

V tomto článku jsme si vysvětlili, co je federation gateway, jaký je její hlavní úkol a jak tuto problematiku řešit za pomocí NX. Ukázali jsme si jak pracovat s databází, jak můžeme automatizovaně vyměňovat schémata neboli supergrafy ve federation gateway a jak supergrafy vytvářet a aktualizovat. Věříme, že vám to pomůže při vaší budoucí tvorbě a že díky tomu předejdete zbytečným errorům.

Autor: Jakub Mikeš, Back-end Developer, Qest
Qest na sociálních sítích: Facebook, Instagram, LinkedIn, Twitter
Přidej se k nám! 👋🏻

--

--

Články ze softwarové firmy Qest technologies. O lidech, IT, technologiích.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store