🔄 REST API vs GraphQL
📖 Définition
REST API est une architecture API traditionnelle conçue autour des ressources utilisant les méthodes HTTP. GraphQL est un langage de requête développé par Facebook qui fournit une approche API flexible où les clients peuvent demander exactement les données dont ils ont besoin. REST a plusieurs points de terminaison, tandis que GraphQL gère toutes les données via un seul point de terminaison.
🎯 Comprendre par analogie
Restaurant vs Buffet
REST API = Menu de restaurant
├─ Peut uniquement commander depuis le menu prédéfini
├─ Chaque menu a une composition fixe
├─ "Une pizza s'il vous plaît" → Obtenir toute la pizza
├─ Difficile de retirer ou ajouter des ingrédients spécifiques
└─ Simple et prévisible
GraphQL = Buffet
├─ Choisissez et prenez seulement ce que vous voulez
├─ Sélectionnez seulement ce dont vous avez besoin
├─ "Juste du fromage s'il vous plaît" → Obtenir seulement du fromage
├─ Combinez librement les éléments
└─ Flexible mais peut être complexe
Prêt de livres en bibliothèque
REST API = Prêt traditionnel
Bibliothécaire : "De quel livre avez-vous besoin ?"
Moi : "Des livres d'informatique s'il vous plaît"
Bibliothécaire : "Voici tous les livres" (10 livres)
Moi : "Je n'ai besoin que du chapitre 1..." (le reste est inutile)
GraphQL = Prêt intelligent
Moi : "Je n'ai besoin que du chapitre 3 du livre d'informatique"
Bibliothécaire : "Voici juste le chapitre 3" (exactement ce qui est nécessaire)
Moi : "Parfait !"
⚙️ Comment ça fonctionne
1. Comparaison des méthodes de requête de données
REST API : Plusieurs points de terminaison
GET /users/1 → Informations utilisateur
GET /users/1/posts → Publications de l'utilisateur
GET /posts/1/comments → Commentaires de la publication
Total de 3 requêtes nécessaires !
GraphQL : Point de terminaison unique
POST /graphql
{
user(id: 1) {
name
posts {
title
comments {
text
}
}
}
}
Toutes les données en 1 requête !
2. Over-fetching vs Under-fetching
Problèmes de REST API
Over-fetching (recevoir des données inutiles)
GET /users/1
{
"id": 1,
"name": "Jean Dupont",
"email": "jean@example.com",
"phone": "01-23-45-67-89",
"address": "123 Rue Principale...",
"createdAt": "2024-01-01",
// Recevoir toutes les informations alors qu'on a besoin seulement du nom !
}
Under-fetching (données insuffisantes)
GET /users/1 → Informations utilisateur
GET /users/1/posts → Requête supplémentaire nécessaire
GET /users/1/friends → Encore une requête nécessaire
// Plusieurs requêtes nécessaires !
Solution GraphQL
Exactement ce qui est nécessaire
{
user(id: 1) {
name // Demander seulement le nom !
}
}
→ { "name": "Jean Dupont" }
Tout en une fois
{
user(id: 1) {
name
posts { title }
friends { name }
}
}
→ Toutes les données en 1 requête !
3. Philosophie de conception d'API
REST : Centré sur les ressources
┌─────────────────┐
│ /users │ → Liste des utilisateurs
│ /users/1 │ → Utilisateur spécifique
│ /posts │ → Liste des publications
│ /posts/1 │ → Publication spécifique
└─────────────────┘
Point de terminaison pour chaque ressource
GraphQL : Centré sur les requêtes
┌─────────────────┐
│ /graphql │ → Toutes les requêtes
└─────────────────┘
↓
┌────────┐
│ Query │ → Lire les données
│Mutation│ → Modifier les données
│Subscribe│ → Abonnement en temps réel
└────────┘
💡 Exemples pratiques
Exemple REST API (Express.js)
// Implémentation REST API avec Express.js
const express = require('express');
const app = express();
app.use(express.json());
// Données (une base de données serait utilisée en pratique)
const users = [
{
id: 1,
name: 'Jean Dupont',
email: 'jean@example.com',
age: 25
},
{
id: 2,
name: 'Marie Martin',
email: 'marie@example.com',
age: 30
}
];
const posts = [
{
id: 1,
userId: 1,
title: 'Introduction à REST API',
content: 'REST est...'
},
{
id: 2,
userId: 1,
title: 'Introduction à GraphQL',
content: 'GraphQL est...'
}
];
// ========== GET : Récupérer tous les utilisateurs ==========
app.get('/api/users', (req, res) => {
res.json(users);
});
// ========== GET : Récupérer un utilisateur spécifique ==========
app.get('/api/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) {
return res.status(404).json({ error: 'Utilisateur non trouvé' });
}
res.json(user);
});
// ========== POST : Créer un utilisateur ==========
app.post('/api/users', (req, res) => {
const newUser = {
id: users.length + 1,
name: req.body.name,
email: req.body.email,
age: req.body.age
};
users.push(newUser);
res.status(201).json(newUser);
});
// ========== PUT : Mettre à jour un utilisateur ==========
app.put('/api/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) {
return res.status(404).json({ error: 'Utilisateur non trouvé' });
}
user.name = req.body.name || user.name;
user.email = req.body.email || user.email;
user.age = req.body.age || user.age;
res.json(user);
});
// ========== DELETE : Supprimer un utilisateur ==========
app.delete('/api/users/:id', (req, res) => {
const index = users.findIndex(u => u.id === parseInt(req.params.id));
if (index === -1) {
return res.status(404).json({ error: 'Utilisateur non trouvé' });
}
users.splice(index, 1);
res.status(204).send();
});
// ========== GET : Récupérer les publications de l'utilisateur ==========
app.get('/api/users/:id/posts', (req, res) => {
const userId = parseInt(req.params.id);
const userPosts = posts.filter(p => p.userId === userId);
res.json(userPosts);
});
// ========== GET : Récupérer une publication spécifique ==========
app.get('/api/posts/:id', (req, res) => {
const post = posts.find(p => p.id === parseInt(req.params.id));
if (!post) {
return res.status(404).json({ error: 'Publication non trouvée' });
}
res.json(post);
});
app.listen(3000, () => {
console.log('Serveur REST API en cours d\'exécution : http://localhost:3000');
});
Exemple GraphQL (Apollo Server)
// Implémentation du serveur GraphQL
const { ApolloServer, gql } = require('apollo-server');
// Données (une base de données serait utilisée en pratique)
const users = [
{ id: 1, name: 'Jean Dupont', email: 'jean@example.com', age: 25 },
{ id: 2, name: 'Marie Martin', email: 'marie@example.com', age: 30 }
];
const posts = [
{ id: 1, userId: 1, title: 'Introduction à REST API', content: 'REST est...' },
{ id: 2, userId: 1, title: 'Introduction à GraphQL', content: 'GraphQL est...' },
{ id: 3, userId: 2, title: 'Tutoriel Node.js', content: 'Node est...' }
];
// ========== Définition du schéma (Type Definitions) ==========
const typeDefs = gql`
# Type Utilisateur
type User {
id: Int!
name: String!
email: String!
age: Int
posts: [Post!]! # Publications de l'utilisateur (relation)
}
# Type Publication
type Post {
id: Int!
title: String!
content: String!
author: User! # Auteur de la publication (relation)
}
# Query (Lire les données)
type Query {
# Tous les utilisateurs
users: [User!]!
# Utilisateur spécifique
user(id: Int!): User
# Toutes les publications
posts: [Post!]!
# Publication spécifique
post(id: Int!): Post
}
# Mutation (Modifier les données)
type Mutation {
# Créer un utilisateur
createUser(name: String!, email: String!, age: Int): User!
# Mettre à jour un utilisateur
updateUser(id: Int!, name: String, email: String, age: Int): User
# Supprimer un utilisateur
deleteUser(id: Int!): Boolean!
# Créer une publication
createPost(userId: Int!, title: String!, content: String!): Post!
}
`;
// ========== Resolvers (Comment récupérer les données) ==========
const resolvers = {
Query: {
users: () => users,
user: (parent, args) => users.find(u => u.id === args.id),
posts: () => posts,
post: (parent, args) => posts.find(p => p.id === args.id)
},
Mutation: {
createUser: (parent, args) => {
const newUser = {
id: users.length + 1,
name: args.name,
email: args.email,
age: args.age
};
users.push(newUser);
return newUser;
},
updateUser: (parent, args) => {
const user = users.find(u => u.id === args.id);
if (!user) return null;
if (args.name) user.name = args.name;
if (args.email) user.email = args.email;
if (args.age) user.age = args.age;
return user;
},
deleteUser: (parent, args) => {
const index = users.findIndex(u => u.id === args.id);
if (index === -1) return false;
users.splice(index, 1);
return true;
},
createPost: (parent, args) => {
const newPost = {
id: posts.length + 1,
userId: args.userId,
title: args.title,
content: args.content
};
posts.push(newPost);
return newPost;
}
},
// ========== Resolvers de relation ==========
User: {
posts: (parent) => posts.filter(p => p.userId === parent.id)
},
Post: {
author: (parent) => users.find(u => u.id === parent.userId)
}
};
// ========== Démarrer le serveur ==========
const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => {
console.log(`Serveur GraphQL en cours d'exécution : ${url}`);
});
🤔 Questions fréquemment posées
Q1. Dois-je choisir REST API ou GraphQL ?
R : Choisissez en fonction des caractéristiques de votre projet :
✅ Choisir REST API quand :
├─ Application CRUD simple
├─ Structure de ressources claire
├─ Cache important (utiliser le cache HTTP)
├─ L'équipe est familière avec REST
├─ Beaucoup de téléchargements/uploads de fichiers
└─ Exemple : Blog, fonctionnalités de base du commerce électronique
✅ Choisir GraphQL quand :
├─ Relations de données complexes
├─ Application mobile (économie de données importante)
├─ Clients divers (web, app, tablette)
├─ Développement frontend rapide souhaité
├─ Abonnement aux données en temps réel nécessaire
└─ Exemple : Médias sociaux, tableau de bord, applications en temps réel
🤝 Peut également être utilisé ensemble :
├─ API principale utilise GraphQL
├─ Upload de fichiers utilise REST
└─ Tirer parti des avantages de chacun
Q2. Quelle est la différence de performance ?
R :
// Caractéristiques de performance de REST API
Avantages :
- Peut utiliser le cache HTTP
GET /api/users/1
Cache-Control: max-age=3600
- Cache CDN facile
- Points de terminaison simples sont rapides
Inconvénients :
- Over-fetching transfère des données inutiles
GET /api/users/1
→ Renvoie tous les champs (100KB)
- Under-fetching nécessite plusieurs requêtes
GET /api/users/1 // 1ère requête
GET /api/users/1/posts // 2ème requête
GET /api/posts/1/comments // 3ème requête
// 3 requêtes au total !
// Caractéristiques de performance de GraphQL
Avantages :
- Seulement les données exactement nécessaires (10KB)
query {
user(id: 1) {
name
email
}
}
- Toutes les données en une requête
query {
user(id: 1) {
name
posts {
title
comments { text }
}
}
}
// Fait en 1 requête !
Inconvénients :
- Cache HTTP difficile (utilise des requêtes POST)
- Requêtes complexes chargent le serveur
- Problème N+1 (résolu avec DataLoader)
Benchmark réel :
REST : 3 requêtes, 200KB total, 300ms
GraphQL : 1 requête, 50KB total, 150ms
→ GraphQL avantageux dans les environnements mobiles
Q3. Qu'est-ce que le problème N+1 dans GraphQL ?
R : Peut être résolu avec DataLoader :
// ========== Problème N+1 ==========
// Interroger 10 utilisateurs et leurs publications
// Problème : Requêtes inefficaces
const resolvers = {
Query: {
users: () => {
return db.users.findAll(); // 1 requête
}
},
User: {
posts: (user) => {
return db.posts.findByUserId(user.id); // 1 requête par utilisateur !
}
}
};
// Total des requêtes : 1 + 10 = 11
// 1 : Récupérer les utilisateurs
// 10 : Récupérer les publications de chaque utilisateur
// ========== Résoudre avec DataLoader ==========
const DataLoader = require('dataloader');
// Récupérer les publications en lot
const postLoader = new DataLoader(async (userIds) => {
// Récupérer toutes les publications en une fois
const posts = await db.posts.findByUserIds(userIds);
// Grouper par utilisateur
const postsByUser = {};
posts.forEach(post => {
if (!postsByUser[post.userId]) {
postsByUser[post.userId] = [];
}
postsByUser[post.userId].push(post);
});
// Renvoyer les publications de chaque utilisateur
return userIds.map(id => postsByUser[id] || []);
});
const resolvers = {
User: {
posts: (user) => {
return postLoader.load(user.id); // Traitement par lots !
}
}
};
// Total des requêtes : 1 + 1 = 2
// 1 : Récupérer les utilisateurs
// 1 : Récupérer toutes les publications (lot)
// → Amélioration significative des performances !
🎓 Prochaines étapes
Maintenant que vous comprenez REST API et GraphQL, essayez d'apprendre :
- Qu'est-ce qu'une API ? (À venir) - Concepts de base des API
- Token JWT (À venir) - Authentification API
- Qu'est-ce que WebSocket ? (À venir) - Communication en temps réel
Pratique
# ========== Pratique REST API (Express) ==========
mkdir rest-api-demo
cd rest-api-demo
npm init -y
npm install express
# Après avoir écrit server.js
node server.js
# Tester
curl http://localhost:3000/api/users
curl -X POST http://localhost:3000/api/users \
-H "Content-Type: application/json" \
-d '{"name":"Jean Dupont","email":"jean@example.com"}'
# ========== Pratique GraphQL (Apollo Server) ==========
mkdir graphql-demo
cd graphql-demo
npm init -y
npm install apollo-server graphql
# Après avoir écrit server.js
node server.js
# Ouvrir GraphQL Playground
# http://localhost:4000
# Exécutez des requêtes dans le navigateur !
# ========== React + GraphQL ==========
npx create-react-app my-app
cd my-app
npm install @apollo/client graphql
# Après la configuration d'Apollo Client
npm start
🎬 Conclusion
REST API et GraphQL ont chacun leurs avantages et inconvénients :
- REST API : Simple, familier et cache facile
- GraphQL : Flexible, efficace et support en temps réel
- Critères de sélection : Complexité du projet, expérience de l'équipe, exigences de performance
- Hybride : Utilisez les deux approches selon les besoins
Créez de superbes applications avec une conception d'API appropriée ! 🔄