De REST à GraphQL, partie 2

Découvrez comment connecter des applications Angular à GraphQL à l’aide d’Apollo, de la configuration et de la simulation à la véritable intégration de l’API REST.
Dans le précédent article sur GraphQLnous avons appris à configurer un serveur GraphQL, à utiliser des exemples de données fictives et les bases de la création de requêtes sur le serveur. Lors de la création d’applications à l’aide de GraphQL, cela modifie le modèle commun pour obtenir des données dans nos applications, en s’éloignant de la création de services avec des méthodes spécifiques pour récupérer des données pour chaque cas de vue ou en renvoyant des données inutiles.
Nous avons deux options : Créer de nouvelles méthodes pour chaque version des données avec des propriétés plus ou moins détaillées, ou apporter toutes les données et utiliser la Carte Opérateur RxJS pour transformer les données, mapper et renvoyer uniquement les champs obligatoires. Cependant, cela se produit après avoir demandé toutes les données, ce qui signifie que la charge de travail liée à l’apport des données ne change pas ; nous ne faisons que le transformer.
GraphQL simplifie le processus à l’aide de requêtes, et aujourd’hui, nous allons apprendre à intégrer Apollo GraphQL dans Angular en utilisant une stratégie de récupération de données moderne et à offrir plus de flexibilité dans la façon dont vos composants frontaux demandent des données en utilisant la puissance de GraphQL et GraphQL Apollo.
Allons-y !
Mise en place du projet
Avant de commencer, rappelez-vous que cet article est une continuation afin que vous puissiez lire la première partie ou continuez en clonant le référentiel et en pointant vers le graphql-server-start bifurquer.
git clone -b graphql-server-start --single-branch https://github.com/danywalls/graphql-and-angular.git
Ensuite, nous devons installer les dépendances pour notre application Angular en exécutant npm install. Notre prochaine étape consiste à installer les packages nécessaires pour connecter notre Angular à GraphQL en utilisant les packages suivants :
- Le
graphqlpackage – iL’implémentation GraphQL du noyau du système d’exploitation @apollo/client– Le package client Apollo avec des capacités de mise en cache@apollo-angular– Liaisons spécifiques à Angular pour le client Apollo
Dans le terminal, exécutez npm install @apollo/client apollo-angular graphql.
dany@mac graphql-and-angular % npm install @apollo/client apollo-angular graphql
up to date, audited 1224 packages in 3s
Run `npm audit` for details.
dany@mac graphql-and-angular %
Une fois cela terminé, exécutez ng serve pour voir le projet se dérouler. Dans votre navigateur, ouvrez http://localhost:4200 et notre projet est opérationnel !


Parfait. Passons à l’exécution du serveur GraphQL. Ouvrez un nouveau terminal dans le même projet d’espace de travail. Déplacer vers le server répertoire et exécuter npm i pour installer les dépendances et npm run start pour initialiser le serveur GraphQL.

Dans votre navigateur, ouvrez le serveur GraphQL dans http://localhost:4000.

Parfait! Nous avons toutes les pièces qui travaillent ensemble, il est donc maintenant temps de configurer Apollo dans notre Angular !
Apollo et angulaire
Avant de commencer à taper du code, je veux expliquer ce qu’est Apollo Angular ou Apollo Client. Apollo Client ou Apollo Angular est un client pour GraphQL permettant de faciliter la récupération de données dans les composants ou services Angular. Il fonctionne avec GraphQL ou Schema agnostique, avec une courbe d’apprentissage incrémentielle partant de requêtes de base et passant à des scénarios complexes, faisant réagir notre application aux changements à l’aide du système réactif RxJS.
Il est temps de passer au codage, alors configurons Angular avec Apollo !
Ouvrez le app.config.ts fichier pour configurer le client Apollo et importer provideApollo, HttpLink pour connecter notre client à notre serveur GraphQL et InMemoryCache aider à stocker les données en cache par défaut dans Apollo 3.0 :
import {provideApollo} from 'apollo-angular';
import {HttpLink} from 'apollo-angular/http';
import {InMemoryCache} from '@apollo/client/core';
Utilisez le provideApollo() fonctionner dans notre fournisseur d’applications. Il est utile d’établir une connexion au serveur GraphQL à l’adresse http://localhost:4000/graphql utiliser le cache en mémoire pour Apollo pour stocker les résultats des requêtes et rendre Apollo disponible via l’injection de dépendances.
provideApollo(() => {
const httpLink = inject(HttpLink);
return {
link: httpLink.create({uri: 'http://localhost:4000/graphql'}),
cache: new InMemoryCache(),
};
}),
Le code final dans app.config.ts ressemble à :
import {provideApollo} from 'apollo-angular';
import {HttpLink} from 'apollo-angular/http';
import {InMemoryCache} from '@apollo/client/core';
import {ApplicationConfig, inject} from "@angular/core";
import {provideHttpClient} from "@angular/common/http";
import {provideRouter} from "@angular/router";
import {routes} from "./app.routes";
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideHttpClient(),
provideApollo(() => {
const httpLink = inject(HttpLink);
return {
link: httpLink.create({uri: 'http://localhost:4000/graphql'}),
cache: new InMemoryCache(),
};
}),
],
};
La configuration est terminée ! Nous sommes prêts à commencer à interroger des données à l’aide du service Apollo !
Service Apollo et requête GraphQL
Avant d’écrire notre première requête, voyons ce qui se passe dans notre application. Dans le navigateur, accédez à l’application http://localhost:4200 et ouvrez les outils de développement. Dans la console, nous imprimons la réponse du serveur.

Jetez un œil à la réponse. Il inclut certains champs inutiles pour notre composant, comme la catégorie, la note et la description.

Je souhaite définir les données exactes dont nos composants ont besoin à l’aide d’une requête GraphQL, alors ouvrons le products.component.ts et supprimer productService et products$:
productsService = inject(ProductsService);
public products$ = this.productsService.products$.pipe(
tap(p => console.log(p))
);
La partie que nous supprimons ressemble donc à cette section barrée :

Et ça finit comme ça :
import {Component, inject} from '@angular/core';
import {ProductsService} from "../../services/products.service";
import {AsyncPipe} from "@angular/common";
import {RouterLink} from "@angular/router";
import {tap} from "rxjs";
@Component({
selector: 'app-products',
imports: [AsyncPipe, RouterLink],
templateUrl: './products.component.html',
styleUrl: './products.component.scss'
})
export class ProductsComponent {
}
Notre prochaine étape consiste à créer une requête en important Apollo et gqlet, pour le débogage des réponses aux requêtes, importez map et tap opérateurs de RxJS.
import {Apollo} from "apollo-angular";
import gql from "graphql-tag";
import {map, tap} from "rxjs";
En utilisant le gql tag, déclarer un PRODUCTS_QUERY. Il lui suffit de id, price, image et title champs. Aucun champ supplémentaire ne sera renvoyé, ce qui réduit votre charge utile.
PRODUCTS_QUERY = gql`
{
products {
id
price
image
title
}
}
`;
Maintenant, modifiez votre composant pour récupérer des données à l’aide d’Apollo au lieu d’un service traditionnel. Nous injectons Apollo service dans product.component.ts.
private readonly apollo = inject(Apollo)
Ensuite, définissez un nouvel observable products$ en utilisant apollo service utilisant le watchQuery qui récupère et traite les données. Définissez le paramètre de requête avec PRODUCTS_QUERY.
public products$ = this.apollo.watchQuery<{ products: Product[] }>({
query: this.PRODUCTS_QUERY
}).valueChanges.pipe(
tap(response => console.log('GraphQL response:', response)),
map(result => result.data.products)
);
L’application Angular réagit aux changements en utilisant
asynctuyau dans le products.component.html.
@if (products$ | async; as products) {
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-8">
@for(product of products; track product) {
<div [routerLink]="['product-detail', product.id]" class="bg-white border border-gray-200 rounded-xl shadow hover:shadow-xl transition-shadow p-6 flex flex-col items-center">
<img [src]="product.image" [width]="180" alt="{{product.title}}" class="mb-5 rounded-lg object-cover h-44 w-44 border border-gray-100 shadow-sm" />
<h3 class="text-lg font-semibold mb-2 text-gray-800 text-center line-clamp-2">{{product.title}}</h3>
<span class="text-green-600 font-bold text-2xl mb-2">${{product.price}}</span>
</div>
}
</div>
}
Que sont Apollon, watchQuery et PRODUCTS_QUERY faire?
Tout d’abord, nous utilisons la requête pour spécifier exactement les champs dont vous avez besoin, évitant ainsi la surextraction. Dans notre requête, nous demandons uniquement id, price, image et title. Si votre interface utilisateur affiche uniquement ces champs, vous n’avez pas besoin de récupérer des données supplémentaires.
La requête est fortement typée avec product tapez, parce qu’Apollo watchQuery renvoie un observable qui se met automatiquement à jour lorsque le cache est modifié, gardant votre interface utilisateur synchronisée avec vos données.
Le code final dans products.component.ts ça ressemble à ça :
import {Component, inject} from '@angular/core';
import {AsyncPipe} from "@angular/common";
import {RouterLink} from "@angular/router";
import {Apollo} from "apollo-angular";
import gql from "graphql-tag";
import {Product} from "../../services/products.service";
import {map, tap} from "rxjs";
@Component({
selector: 'app-products',
imports: [AsyncPipe, RouterLink],
templateUrl: './products.component.html',
styleUrl: './products.component.scss'
})
export class ProductsComponent {
PRODUCTS_QUERY = gql`
{
products {
id
price
image
title
}
}`
private readonly apollo = inject(Apollo)
public products$ = this.apollo.watchQuery<{ products: Product[] }>({
query: this.PRODUCTS_QUERY
}).valueChanges.pipe(
tap(response => console.log('GraphQL response:', response)),
map(result => result.data.products)
);
}
Vérifiez que le serveur GraphQL s’exécute sur http://localhost:4000. Sinon, accédez au répertoire du serveur dans l’espace de travail et exécutez
npm start.

Démarrez l’application angulaire ng serveaccédez à http://localhost:4200ouvrez DevTools et vérifiez la console du navigateur. Vous devriez voir la réponse GraphQL enregistrée avec uniquement les champs obligatoires pour notre interface utilisateur. Ouais!!

Oui, notre application Angular demande des données à GraphQL Mock. Si on ouvre les DevTools dans l’onglet console, on voit deux éléments avec uniquement les champs de la requête ! Fini les champs supplémentaires ou inutiles.

Mmmm… ça a l’air bien, mais pour notre application, nous voulons peut-être afficher une liste de produits plus longue pendant que le backend continue de travailler sur d’autres choses. Améliorons-le.
Ouvrez le server/src/schema-mocks.ts. Nous utilisons addMocksToSchemamais lorsque nous ne spécifions pas de simulations pour les champs de liste, @graphql-tools/mock renvoie des listes avec une longueur par défaut de 2. C’est la raison pour laquelle nous voyons deux éléments à moins que nous ne les remplacions par Liste fictive.
import {typeDefs} from "./schema";
import {makeExecutableSchema} from "@graphql-tools/schema";
import {addMocksToSchema} from "@graphql-tools/mock";
const schema = makeExecutableSchema({typeDefs});
const mocks = {
Product: () => ({
id: `1`,
title: 'Mock Product',
price: 15.2,
description: 'This is a mocked product description.',
category: 'Electronics',
image: 'https://fakestoreapi.com/img/81fPKd-2AYL._AC_SL1500_t.png',
rating: {
rate: 5,
count: 3,
},
}),
};
export const schemaWithMocks = addMocksToSchema({schema, mocks});
Remplacez la requête dans les produits et utilisez le MockList fonction pour renvoyer 15 éléments.
Query: () => ({
products: () => new MockList(15),
}),
Le code final ressemble à :
import {typeDefs} from "./schema";
import {makeExecutableSchema} from "@graphql-tools/schema";
import {addMocksToSchema, MockList} from "@graphql-tools/mock";
const schema = makeExecutableSchema({typeDefs});
const mocks = {
Query: () => ({
products: () => new MockList(15)
}),
Product: () => ({
id: `1`,
title: 'Mock Product',
price: 15.2,
description: 'This is a mocked product description.',
category: 'Electronics',
image: 'https://fakestoreapi.com/img/81fPKd-2AYL._AC_SL1500_t.png',
rating: {
rate: 5,
count: 3,
},
}),
};
export const schemaWithMocks = addMocksToSchema({schema, mocks});
Enregistrez les modifications dans le schema-mocks.tsarrêtez et démarrez le serveur GraphQL. Après cela, nous pouvons recharger l’application Angular et obtenir maintenant 15 éléments !

Encore une chose. Je veux aller plus loin. Nous savons désormais comment écrire des requêtes et utiliser des données fictives, mais dans le monde réel, vous ne travaillerez pas uniquement avec des données fictives. Vous devez connecter une véritable API REST avec GraphQL. Faisons notre dernier chapitre intégrant GraphQL avec une API REST.
Intégrer GraphQL à l’API REST
Lorsque nous avons construit notre serveur GraphQL pour faciliter notre processus de développement, nous utilisons des simulations, server/src/schema-mocks.ts en utilisant @graphql-tools/mock. Mais pour interroger les données réelles de l’API REST, quelque chose doit prendre la responsabilité d’obtenir ces informations dans GraphQL et celles-ci sont resolvers.
Les résolveurs sont des fonctions qui indiquent à GraphQL comment récupérer les données d’un champ particulier de votre schéma. Lorsqu’une requête est exécutée, GraphQL parcourt l’arborescence de requête et appelle des fonctions de résolution pour produire des valeurs pour chaque champ.
Si vous ne fournissez pas de résolveur pour un champ, GraphQL utilise des résolveurs par défaut qui renvoient simplement la propriété du même nom à partir de l’objet parent (par exemple, pour Product.title ça revient parent.title). Cela fonctionne bien lorsque la forme de vos données correspond déjà à votre schéma.
Créez le résolveur pour notre schéma ; ouvrez le fichier (server/src/schema.ts). Définir un objet résolveur et implémenter Query.products en utilisant le fetch fonction pour demander les données à l’API.
export const resolvers = {
Query: {
products: async () => {
const url = 'https://fakestoreapi.com/products';
const res = await fetch(url);
if (!res.ok) {
throw new Error(`Failed to fetch products: ${res.status} ${res.statusText}`);
}
return await res.json();
},
},
};
Ouvrez le server/src/index.ts, il a un schéma : schemaWithMocks dans l’instanciation ApolloServer ; nous allons y apporter un petit changement.
import {ApolloServer} from "@apollo/server";
import {startStandaloneServer} from "@apollo/server/standalone";
import {schemaWithMocks} from "./schema-mock";
async function startApolloServer() {
const server = new ApolloServer({
schema: schemaWithMocks
});
const {url} = await startStandaloneServer(server);
console.log(`
🚀 Server is running at ${url}
`);
}
startApolloServer();
Tout d’abord, retirez le schema: schemaWithMocks et réglez le typeDef et resolvers de la schema déposer.
La version finale ressemble à :
import {ApolloServer} from "@apollo/server";
import {startStandaloneServer} from "@apollo/server/standalone";
import {typeDefs, resolvers} from "./schema";
async function startApolloServer() {
const server = new ApolloServer({
typeDefs,
resolvers,
});
const {url} = await startStandaloneServer(server);
console.log(`
🚀 Server is running at ${url}
`);
}
startApolloServer();
Enregistrez les modifications et n’oubliez pas d’arrêter et de démarrer le serveur GraphQL. Après cela, nous pouvons recharger l’application Angular connectée avec une API REST en utilisant GraphQL !!!

Résumer
Ce fut un long voyage avec Angular et GraphQL. C’était tellement amusant !
Nous avons appris dès le début pourquoi et quand utiliser GraphQL, en commençant par la configuration de base de GraphQL, en nous moquant de la réponse du serveur et comment intégrer GraphQL avec Angular à l’aide d’Apollo pour effectuer une récupération de données précise, en demandant les champs exacts nécessaires.
Nous avons utilisé la puissance de la requête pour montrer clairement de quelles données le composant a besoin tandis que le backend peut évoluer sans casser le frontend, à condition qu’il prenne en charge les champs demandés.
Enfin, nous avons connecté GraphQL à une API REST pour effectuer des requêtes réelles comme vous le feriez dans votre travail.
Source:
Le Kendo Store a été construit facilement à l’aide de l’interface utilisateur Progress Kendo pour les composants angulaires. Prêt à voir ce que vous pouvez créer avec Kendo UI pour Angular ? Il est livré avec un essai gratuit de 30 jours.
Source link
