Angular avec GraphQL : Un Tutoriel Intermédiaire en 30 Minutes
Pourquoi Angular avec GraphQL ?
Au quotidien, un développeur Angular peut avoir besoin de récupérer et d'afficher des données complexes à partir de différentes sources, comme une base de données, une API REST ou même une APIGraphQL. Avec le temps, il peut s'apercevoir que ses requêtes HTTP sont devenues plus complexes et nécessitent plus de gestion. C'est là que l'Angular avec GraphQL est le bon choix.
En utilisant GraphQL, un développeur Angular peut récupérer exactement les données dont il a besoin, sans avoir à effectuer plusieurs appels d'API individuels. Ce qui signifie qu'il peut rafraîchir les données en une seule requête. De plus, avec GraphQL, le serveur ne renvoie que les données nécessaires, ce qui réduit la taille des données envoyées et augmente ainsi l'efficacité globale de l'application.
Un cas d'usage concret serait l'affichage d'une liste d'utilisateurs avec leurs informations comme leur nom, email et rôle. Avec une API REST traditionnelle, cela nécessiterait plusieurs requêtes pour obtenir ces informations. Avec GraphQL, il suffit simplement d'effectuer une seule requête pour récupérer toutes les données nécessaires.
Prerequis
- Connaissance de base d'Angular (version 10+)
- Connaissance de l'utilisation des services HTTP en Angular
- Familiarité avec les types et interfaces en TypeScript
- Node.js installé sur votre ordinateur
- Un éditeur de code (comme Visual Studio Code)
Concepts fondamentaux
1. GraphQL Server
Un serveur GraphQL est une API qui utilise le langage de requêtes GraphQL pour récupérer des données. Il permet aux clients d'effectuer des requêtes précises et de recevoir uniquement les données nécessaires.
// server.ts
import { ApolloServer, gql } from 'apollo-server';
const typeDefs = gql`
type User {
id: ID!
name: String!
email: String!
}
type Query {
getUser(id: ID!): User
}
`;
const resolvers = {
Query: {
getUser: (_, { id }) => {
// Simulate fetching data from a database
return {
id,
name: 'John Doe',
email: 'john.doe@example.com'
};
}
}
};
const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
2. GraphQL Client
Le client GraphQL est utilisé pour effectuer des requêtes sur le serveur GraphQL et récupérer les données nécessaires.
// app.component.ts
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-root',
template: `
<div *ngIf="user">
ID: user.id<br>
Name: user.name<br>
Email: user.email
</div>
`
})
export class AppComponent {
user;
constructor(private http: HttpClient) {
this.http.get('http://localhost:4000/graphql', {
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: `
query GetUser($id: ID!) {
getUser(id: $id) {
id
name
email
}
}
`,
variables: { id: '1' }
})
}).subscribe(data => {
this.user = data.data.getUser;
});
}
}
3. Apollo Client
Apollo Client est une bibliothèque JavaScript qui simplifie la gestion des données GraphQL dans les applications React et Angular.
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { ApolloModule, APOLLO_OPTIONS } from 'apollo-angular';
import { HttpLinkModule, HttpLink } from 'apollo-angular-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule,
ApolloModule,
HttpLinkModule
],
providers: [
{
provide: APOLLO_OPTIONS,
useFactory: (httpLink: HttpLink) => {
return {
link: httpLink.create({
uri: 'http://localhost:4000/graphql'
}),
cache: new InMemoryCache()
};
},
deps: [HttpLink]
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
typescript
// app.component.ts
import { Component } from '@angular/core';
import { Apollo } from 'apollo-angular';
@Component({
selector: 'app-root',
template: `
<div *ngIf="user">
ID: user.id<br>
Name: user.name<br>
Email: user.email
</div>
`
})
export class AppComponent {
user;
constructor(private apollo: Apollo) {
this.apollo.watchQuery({
query: gql`
query GetUser($id: ID!) {
getUser(id: $id) {
id
name
email
}
}
`,
variables: { id: '1' }
}).valueChanges.subscribe(data => {
this.user = data.data.getUser;
});
}
}
Mise en pratique : Projet fil rouge
Étape 1 : Création du projet Angular
ng new angular-graphql-app
cd angular-graphql-app
Étape 2 : Installation d'Apollo Client
npm install apollo-angular apollo-angular-link-http apollo-cache-inmemory graphql-tag graphql
Étape 3 : Configuration d'Apollo Client
Créez un fichier apollo.config.ts dans le dossier src/environments/.
// src/environments/environment.ts
export const environment = {
production: false,
apiUrl: 'http://localhost:4000/graphql'
};
Étape 4 : Configuration de l'application
Modifiez app.module.ts pour inclure Apollo Client.
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { ApolloModule, APOLLO_OPTIONS } from 'apollo-angular';
import { HttpLinkModule, HttpLink } from 'apollo-angular-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { AppComponent } from './app.component';
import { environment } from '../environments/environment';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule,
ApolloModule,
HttpLinkModule
],
providers: [
{
provide: APOLLO_OPTIONS,
useFactory: (httpLink: HttpLink) => {
return {
link: httpLink.create({
uri: environment.apiUrl
}),
cache: new InMemoryCache()
};
},
deps: [HttpLink]
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
Étape 5 : Création d'un service GraphQL
Créez un fichier user.service.ts dans le dossier src/app/.
// src/app/user.service.ts
import { Injectable } from '@angular/core';
import { Apollo, gql } from 'apollo-angular';
@Injectable({
providedIn: 'root'
})
export class UserService {
constructor(private apollo: Apollo) {}
getUser(id: string) {
return this.apollo.watchQuery<any>({
query: gql`
query GetUser($id: ID!) {
getUser(id: $id) {
id
name
email
}
}
`,
variables: { id }
}).valueChanges;
}
}
Étape 6 : Création d'un composant pour afficher les utilisateurs
Créez un fichier user.component.ts et user.component.html dans le dossier src/app/.
// src/app/user.component.ts
import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';
@Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
user;
constructor(private userService: UserService) {}
ngOnInit() {
this.userService.getUser('1').subscribe(data => {
this.user = data.data.getUser;
});
}
}
html
<!-- src/app/user.component.html -->
<div *ngIf="user">
ID: user.id<br>
Name: user.name<br>
Email: user.email
</div>
Étape 7 : Ajout du composant à la route
Modifiez app-routing.module.ts pour ajouter une route pour le composant UserComponent.
// src/app/app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { UserComponent } from './user/user.component';
const routes: Routes = [
{ path: 'user/:id', component: UserComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Étape 8 : Ajout de la route à l'application
Modifiez app.component.html pour ajouter une barre de navigation avec une route vers le composant UserComponent.
<!-- src/app/app.component.html -->
<nav>
<a routerLink="/user/1">User</a>
</nav>
<router-outlet></router-outlet>
Erreurs fréquentes et debugging
1. Erreur : Syntaxe de la requête GraphQL incorrecte
## ❌ Mauvais
query {
getUser(id: "1")
}
## ✅ Correct
query GetUser($id: ID!) {
getUser(id: $id) {
id
name
email
}
}
2. Erreur : Le serveur GraphQL est inaccessible
## ❌ Mauvais
const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
## ✅ Correct
const server = new ApolloServer({ typeDefs, resolvers });
server.listen().catch(err => {
console.error('Server failed to start', err);
});
3. Erreur : Le client GraphQL ne peut pas se connecter au serveur
## ❌ Mauvais
this.http.get('http://localhost:4000/graphql', {
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: `
query GetUser($id: ID!) {
getUser(id: $id) {
id
name
email
}
}
`,
variables: { id: '1' }
})
}).subscribe(data => {
this.user = data.data.getUser;
});
## ✅ Correct
this.http.post('http://localhost:4000/graphql', {
query: `
query GetUser($id: ID!) {
getUser(id: $id) {
id
name
email
}
}
`,
variables: { id: '1' }
}, {
headers: { 'Content-Type': 'application/json' }
}).subscribe(data => {
this.user = data.data.getUser;
});
Pour aller plus loin
1. Optimisation des requêtes GraphQL avec le batching
Le batching permet de regrouper plusieurs requêtes en une seule requête pour améliorer les performances.
// server.ts
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => {
return { token: req.headers.authorization };
},
plugins: [
ApolloServerPluginBatchHttp()
]
});
2. Authentification et autorisation avec GraphQL
L'authentification et l'autorisation sont essentiels pour protéger les données sensibles.
// server.ts
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => {
const token = req.headers.authorization;
return { user: jwt.verify(token, 'secret') };
},
plugins: [
ApolloServerPluginAuthentication({
getUser: async (ctx) => {
const token = ctx.req.headers.authorization;
if (token) {
try {
return await jwt.verify(token, 'secret');
} catch (e) {}
}
return null;
}
})
]
});
3. Utilisation de directives GraphQL
Les directives permettent d'ajouter des fonctionnalités spécifiques aux requêtes GraphQL.
// schema.graphql
directive @cacheControl(maxAge: Int!) on FIELD_DEFINITION
type User {
id: ID!
name: String!
email: String! @cacheControl(maxAge: 3600)
}
Défi pratique : Créer un service pour récupérer des tâches à faire
Créez un service task.service.ts qui utilise GraphQL pour récupérer et gérer les tâches à faire. Ajoutez une méthode pour obtenir toutes les tâches, une méthode pour ajouter une nouvelle tâche et une méthode pour marquer une tâche comme terminée.
// src/app/task.service.ts
import { Injectable } from '@angular/core';
import { Apollo, gql } from 'apollo-angular';
@Injectable({
providedIn: 'root'
})
export class TaskService {
constructor(private apollo: Apollo) {}
getTasks() {
return this.apollo.watchQuery<any>({
query: gql`
query GetTasks {
tasks {
id
title
description
completed
}
}
`
}).valueChanges;
}
addTask(title: string, description: string) {
return this.apollo.mutate({
mutation: gql`
mutation AddTask($title: String!, $description: String!) {
addTask(title: $title, description: $description) {
id
title
description
completed
}
}
`,
variables: { title, description },
refetchQueries: ['GetTasks']
});
}
completeTask(id: string) {
return this.apollo.mutate({
mutation: gql`
mutation CompleteTask($id: ID!) {
completeTask(id: $id) {
id
title
description
completed
}
}
`,
variables: { id },
refetchQueries: ['GetTasks']
});
}
}
Ce tutoriel vous a montré comment utiliser Angular avec GraphQL pour récupérer et afficher des données complexes. Vous avez appris les concepts de base de GraphQL, comment configurer Apollo Client dans une application Angular, et comment créer un service pour gérer des données en utilisant GraphQL. Vous devriez maintenant être capable de créer des applications plus efficaces et performantes en utilisant Angular avec GraphQL.