A modern webalkalmazások fejlesztése során az adatkommunikáció kulcsfontosságú. Ahogy az alkalmazások egyre komplexebbé válnak, úgy nő az igény a rugalmas, hatékony és karbantartható API-kra. A REST API-k hosszú ideig uralták ezt a területet, de a kihívások – mint például az adat túl- vagy alulekérdezése (over-fetching, under-fetching) és a számos végpont kezelése – új megoldások felé terelték a fejlesztőket. Itt jön képbe a GraphQL, egy lekérdezési nyelv az API-k számára, amely forradalmasítja az adatlekérdezést és -kezelést. Ebben a cikkben részletesen megvizsgáljuk, hogyan integrálhatjuk és használhatjuk a GraphQL-t Angular alapú alkalmazásainkban, kiemelve annak előnyeit és a legjobb gyakorlatokat.
Mi az a GraphQL és miért érdemes használni Angularral?
A GraphQL-t a Facebook fejlesztette ki 2012-ben (majd 2015-ben nyílt forráskódúvá tette), elsősorban a mobilalkalmazásaik növekvő adatigényeinek kiszolgálására. Lényegében egy specifikáció arról, hogyan kérhetünk le adatokat egy szervertől, és hogyan módosíthatjuk azokat. A legfontosabb különbség a REST-hez képest, hogy a GraphQL egyetlen végpontot használ, és a kliens pontosan megadhatja, milyen adatokat szeretne lekérdezni, és milyen formában. Ez kiküszöböli azokat a problémákat, amikor a REST API túl sok vagy túl kevés adatot küld vissza, ami optimalizáltabb hálózati forgalmat és gyorsabb alkalmazásokat eredményez.
Az Angular, mint egy robusztus és strukturált frontend keretrendszer, tökéletes partner a GraphQL számára. Az Angular komponensalapú architektúrája és az RxJS reaktív programozási mintái kiválóan illeszkednek a GraphQL adatfolyam-kezeléséhez. A GraphQL lehetővé teszi, hogy az Angular alkalmazások sokkal specifikusabb és hatékonyabb adatlekérdezéseket végezzenek, csökkentve a szerveroldali terhelést és javítva a felhasználói élményt.
A GraphQL fő előnyei Angular alkalmazásokban:
- Precíz adatlekérdezés: A kliens pontosan azt kapja vissza, amire szüksége van, sem többet, sem kevesebbet. Ez csökkenti a hálózati forgalmat és gyorsítja az alkalmazást.
- Kevesebb lekérdezés: Gyakran több REST végpontot kellene meghívni egy komplex nézet betöltéséhez, míg GraphQL-lel ez egyetlen lekérdezéssel megoldható.
- Fejlesztői élmény: A GraphQL séma (schema) leírja az API összes képességét, így a frontend fejlesztők könnyedén felfedezhetik és használhatják azt, gyakran kódgenerálással is kiegészítve.
- Típusbiztonság: A GraphQL séma erős típusbiztonságot nyújt, ami csökkenti a futásidejű hibákat és javítja a kód minőségét.
- Valós idejű adatok: Az előfizetések (Subscriptions) segítségével könnyedén kezelhetők a valós idejű adatfrissítések, ami kritikus funkció számos modern alkalmazásban (pl. chatek, értesítések).
- Gyorsabb iteráció: A séma evolúciója könnyebb, mint a REST API verziózása, mivel a kliens kódja csak a számára szükséges mezőket érinti.
A GraphQL beállítása Angular alkalmazásokban: Az Apollo Angular
Az Angular és a GraphQL összekapcsolásához a legnépszerűbb és legelterjedtebb klienskönyvtár az Apollo Angular. Ez az Apollo Client implementációja Angularra, amely kiválóan integrálódik az RxJS-sel és az Angular ökoszisztémájával. Kezelni tudja a lekérdezéseket (queries), mutációkat (mutations) és előfizetéseket (subscriptions), valamint fejlett cache mechanizmusokat kínál.
Telepítés és Alapkonfiguráció
Először is telepítenünk kell az Apollo Angular csomagokat az Angular alkalmazásunkba. Nyissunk meg egy terminált a projekt gyökérkönyvtárában, és futtassuk a következő parancsot:
ng add apollo-angular
Ez a parancs automatikusan telepíti a szükséges függőségeket (apollo-angular
, @apollo/client
, graphql
, graphql-tag
) és elvégzi az alapvető konfigurációt az app.module.ts
fájlban. Ha kézzel szeretnénk telepíteni, a következő parancsokra van szükségünk:
npm install apollo-angular @apollo/client graphql-tag graphql
A konfigurációhoz az app.module.ts
fájlban kell beállítanunk az ApolloClient példányt. Ez általában a GraphQL szerver URL-jének megadását jelenti, valamint a cache beállítását. Az ng add
parancs által generált kód valami ilyesmi lesz:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { APOLLO_OPTIONS, ApolloModule } from 'apollo-angular';
import { HttpLink } from 'apollo-angular/http';
import { InMemoryCache } from '@apollo/client/core';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
ApolloModule
],
providers: [
{
provide: APOLLO_OPTIONS,
useFactory: (httpLink: HttpLink) => {
return {
cache: new InMemoryCache(),
link: httpLink.create({
uri: 'http://localhost:4000/graphql', // Itt add meg a GraphQL szervered URL-jét
}),
};
},
deps: [HttpLink],
},
],
bootstrap: [AppComponent]
})
export class AppModule { }
Ez a konfiguráció beállít egy HttpLink
-et a megadott GraphQL szerver URI-jára, és egy InMemoryCache
-t, amely automatikusan tárolja és kezeli a lekérdezések eredményeit. Ez a cache kritikus fontosságú a teljesítmény és a felhasználói élmény szempontjából, mivel megakadályozza a felesleges hálózati kéréseket ugyanazokért az adatokért.
Adatok lekérdezése GraphQL-lel: Queries
A GraphQL-ben az adatlekérdezéseket „queries”-nek nevezzük. Ezek nagyon hasonlítanak a REST GET kéréseihez, de sokkal precízebbek. Az Apollo Angular segítségével egyszerűen végrehajthatunk lekérdezéseket. Először is definiálnunk kell a GraphQL lekérdezést a gql
tag használatával, ami a graphql-tag
könyvtárból érkezik.
import { Component, OnInit } from '@angular/core';
import { Apollo, gql } from 'apollo-angular';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
interface Character {
id: string;
name: string;
species: string;
}
interface AllCharacters {
characters: Character[];
}
const GET_CHARACTERS = gql`
query GetAllCharacters {
characters {
id
name
species
}
}
`;
@Component({
selector: 'app-characters',
template: `
Karakterek
Adatok betöltése...
Hiba történt: {{ error.message }}
-
{{ character.name }} ({{ character.species }})
`,
})
export class CharactersComponent implements OnInit {
characters: Character[] | undefined;
loading: boolean = true;
error: any;
constructor(private apollo: Apollo) {}
ngOnInit() {
this.apollo
.watchQuery({
query: GET_CHARACTERS,
})
.valueChanges.subscribe(({ data, loading, error }) => {
this.loading = loading;
this.error = error;
if (data) {
this.characters = data.characters;
}
});
}
}
Ebben a példában a GET_CHARACTERS
lekérdezést használjuk karakterek listájának lekérdezésére. Az apollo.watchQuery
metódus egy Observable
-t ad vissza, amely frissül, amikor az adatok változnak (például a cache frissítése miatt). A valueChanges
property egy olyan Observable
-t ad, amely a lekérdezés eredményét tartalmazó objektumot bocsátja ki, benne az data
, loading
és error
property-kkel. Ezeket felhasználva könnyedén kezelhetjük a betöltési állapotot és a hibákat a felhasználói felületen.
Adatok módosítása GraphQL-lel: Mutations
Az adatok módosítására, hozzáadására vagy törlésére a mutations-t használjuk a GraphQL-ben. Ezek a REST POST, PUT, PATCH, DELETE metódusainak felelnek meg. A mutációk is hasonlóan működnek a lekérdezésekhez, de a apollo.mutate
metódust használjuk.
import { Component } from '@angular/core';
import { Apollo, gql } from 'apollo-angular';
const ADD_CHARACTER = gql`
mutation AddCharacter($name: String!, $species: String!) {
addCharacter(name: $name, species: $species) {
id
name
species
}
}
`;
@Component({
selector: 'app-add-character',
template: `
Karakter hozzáadása
{{ addedCharacter.name }} hozzáadva (ID: {{ addedCharacter.id }})
Hiba: {{ error.message }}
`,
})
export class AddCharacterComponent {
name: string = '';
species: string = '';
addedCharacter: any;
error: any;
constructor(private apollo: Apollo) {}
addCharacter() {
this.apollo
.mutate({
mutation: ADD_CHARACTER,
variables: {
name: this.name,
species: this.species,
},
// Frissítjük a cache-t, hogy a GET_CHARACTERS lekérdezés is frissüljön
update: (store, { data: { addCharacter } }) => {
const data: any = store.readQuery({ query: GET_CHARACTERS });
if (data && data.characters) {
store.writeQuery({
query: GET_CHARACTERS,
data: { characters: [...data.characters, addCharacter] },
});
}
},
})
.subscribe({
next: ({ data }) => {
this.addedCharacter = data ? data.addCharacter : null;
this.name = '';
this.species = '';
},
error: (error) => {
this.error = error;
},
});
}
}
A mutációk esetében kulcsfontosságú a cache frissítése. Miután sikeresen hozzáadtunk, módosítottunk vagy töröltünk egy elemet, az Apollo cache-t is frissíteni kell, hogy a már létező lekérdezések, amelyek érintettek, is a legfrissebb adatokkal rendelkezzenek. Ezt az update
funkcióval tehetjük meg, amely hozzáférést biztosít a cache-hez. Ebben a példában manuálisan hozzáadjuk az új karaktert a GET_CHARACTERS
lekérdezés cache-elt adataihoz.
Valós idejű adatok kezelése: Subscriptions
A GraphQL Subscriptions lehetővé teszik a kliens számára, hogy valós idejű frissítéseket kapjon a szervertől, amikor bizonyos adatok változnak. Ez ideális chatek, értesítések, élő statisztikák vagy bármilyen valós idejű adatfolyam kezelésére. A subscriptions általában WebSocket kapcsolaton keresztül működnek.
WebSocket beállítása
Az Apollo Angular alapértelmezett konfigurációja csak HTTP linket tartalmaz. A subscriptions használatához telepítenünk kell a WebSocket linket és módosítanunk kell az app.module.ts
konfigurációját:
npm install @apollo/client/link/ws subscriptions-transport-ws
Ezután módosítjuk a providers
részt az app.module.ts
-ben:
import { split } from '@apollo/client/core';
import { WebSocketLink } from '@apollo/client/link/ws';
import { getMainDefinition } from '@apollo/client/utilities';
// ... (többi import)
@NgModule({
// ...
providers: [
{
provide: APOLLO_OPTIONS,
useFactory: (httpLink: HttpLink) => {
const http = httpLink.create({
uri: 'http://localhost:4000/graphql',
});
const ws = new WebSocketLink({
uri: `ws://localhost:4000/graphql`, // WebSocket szerver URI-je
options: {
reconnect: true,
},
});
// A split funkció lehetővé teszi, hogy eldöntsük,
// melyik linket használjuk a kérés típusától függően.
// Queries és Mutations mennek HTTP-n, Subscriptions WebSocket-en.
const link = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
ws,
http,
);
return {
cache: new InMemoryCache(),
link,
};
},
deps: [HttpLink],
},
],
// ...
})
export class AppModule { }
Előfizetés használata
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Apollo, gql } from 'apollo-angular';
import { Subscription } from 'rxjs';
interface Character {
id: string;
name: string;
}
interface CharacterAdded {
characterAdded: Character;
}
const CHARACTER_ADDED_SUBSCRIPTION = gql`
subscription OnCharacterAdded {
characterAdded {
id
name
}
}
`;
@Component({
selector: 'app-character-feed',
template: `
Új karakterek
Új karakter érkezett: {{ newCharacter.name }} (ID: {{ newCharacter.id }})
Várakozás új karakterekre...
Hiba: {{ error.message }}
`,
})
export class CharacterFeedComponent implements OnInit, OnDestroy {
newCharacter: Character | undefined;
error: any;
private subscription: Subscription | undefined;
constructor(private apollo: Apollo) {}
ngOnInit() {
this.subscription = this.apollo
.subscribe({
query: CHARACTER_ADDED_SUBSCRIPTION,
})
.subscribe({
next: ({ data }) => {
if (data) {
this.newCharacter = data.characterAdded;
}
},
error: (error) => {
this.error = error;
console.error('Subscription error', error);
},
});
}
ngOnDestroy() {
this.subscription?.unsubscribe();
}
}
Az apollo.subscribe
metódus egy Observable
-t ad vissza, akárcsak a watchQuery
. Fontos, hogy az előfizetést az ngOnDestroy
életciklus-hookban szüntessük meg, hogy elkerüljük a memóriaszivárgást és a felesleges hálózati forgalmat.
Haladó tippek és legjobb gyakorlatok
- Hiba kezelés: Az Apollo Angular lehetővé teszi, hogy globálisan kezeljük a GraphQL hibákat az ErrorLink segítségével. Ez központosított hibaüzenetek megjelenítésére vagy logolására használható.
- Kódgenerálás: A GraphQL Code Generator (graphql-code-generator.com) egy rendkívül hasznos eszköz, amely automatikusan TypeScript interfészeket és Apollo Service-eket generál a GraphQL sémából és a lekérdezésekből. Ez jelentősen javítja a típusbiztonságot és a fejlesztői élményt, csökkentve a manuális típusdefiníciók szükségességét.
- Hitelesítés és jogosultság: Az Apollo Link Chain segítségével könnyedén hozzáadhatunk hitelesítési tokeneket a GraphQL kérésekhez (pl.
Authorization
header), vagy kezelhetjük a refresh token logikát. - Cache optimalizálás: Az
InMemoryCache
nagyon hatékony, de összetettebb esetekben szükség lehet egyedi cache stratégiákra. Például, ha paginationt használunk, aTypePolicies
segítségével beállíthatjuk, hogyan fűzze össze a cache a különböző lapokat. - Szerveroldali renderelés (SSR): Angular Universal és GraphQL kombinációjával javíthatjuk az alkalmazás SEO-ját és a kezdeti betöltési sebességet. Az Apollo támogatja az SSR-t, lehetővé téve az adatok előzetes betöltését a szerveren.
Mikor érdemes GraphQL-t használni Angularral?
A GraphQL nem minden alkalmazáshoz a legjobb választás, de bizonyos esetekben jelentős előnyöket kínál:
- Komplex adathálózat: Ha az alkalmazásnak sok különböző adatforrásból kell adatokat kombinálnia, és a kliensnek rugalmasan kell tudnia kiválasztani a szükséges mezőket.
- Több platform: Ha ugyanazt az API-t kell használni webes, mobil és egyéb kliensek számára, ahol mindegyiknek eltérő adatigényei lehetnek.
- Gyors iteráció és változó követelmények: A GraphQL séma könnyen bővíthető, és a kliensek továbbra is a régi lekérdezéseket használhatják, amíg a szükséges mezők elérhetők.
- Valós idejű funkciók: Chatek, élő frissítések, értesítések egyszerűen megvalósíthatók subscriptions segítségével.
Egyszerű CRUD (Create, Read, Update, Delete) műveleteket végző, kis méretű alkalmazások esetében a REST API továbbra is megfelelő lehet, és kevesebb overhead-del járhat a beállítás és karbantartás. Fontos mérlegelni a projekt komplexitását és a csapat ismereteit a technológia kiválasztásakor.
Konklúzió
A GraphQL Angular alkalmazásokban való használata egy modern és hatékony megközelítés az adatkommunikációra. Az Apollo Angular kliens robusztus és jól integrált megoldást kínál a lekérdezések, mutációk és előfizetések kezelésére, miközben kihasználja az Angular reaktív képességeit. A precíz adatlekérdezés, a kevesebb hálózati forgalom, a kiváló fejlesztői élmény és a valós idejű adatok támogatása mind olyan előnyök, amelyek a GraphQL-t rendkívül vonzóvá teszik komplex és skálázható Angular alkalmazások építése során. Ha egy igazán dinamikus és adatintenzív webes megoldáson dolgozik, érdemes alaposan megfontolnia a GraphQL bevezetését, és megtapasztalnia az általa kínált szabadságot és hatékonyságot.
Leave a Reply