
REST API
L'architecture REpresentational State Transfer qui définit des standards pour la communication entre systèmes web avec des interfaces simples, évolutives et fiables.
Qu'est-ce qu'une REST API ?
Imaginez que vous allez au restaurant. Vous avez un menu (une liste de ressources disponibles), vous passez une commande (vous faites une requête), et le serveur vous apporte votre plat (la réponse). Vous n'avez pas besoin de savoir comment fonctionne la cuisine ou comment le plat est préparé.
Une API REST fonctionne de manière similaire. C'est une interface qui permet à différents systèmes informatiques de communiquer entre eux sur internet de manière standardisée, sans avoir à connaître les détails techniques de l'autre système.
Pourquoi les API REST sont importantes ?
Universalité
Elles permettent à des applications écrites dans des langages différents et sur des plateformes différentes de communiquer facilement.
Simplicité
Elles utilisent des standards web simples (HTTP, JSON) que la plupart des développeurs connaissent déjà.
En résumé, les API REST sont comme un langage commun qui permet à différentes applications de partager des informations et des fonctionnalités, ce qui est essentiel pour l'écosystème moderne d'applications web, mobiles et de services cloud interconnectés.
Fonctionnement technique
REST (Representational State Transfer) est un style d'architecture pour les systèmes distribués, défini par Roy Fielding dans sa thèse de doctorat en 2000. Une API RESTful s'appuie sur le protocole HTTP et ses méthodes pour exposer des ressources via des URLs standardisées, souvent utilisée avec Node.js ou d'autres langages back-end.
Les principes fondamentaux
Architecture client-serveur sans état
Chaque requête du client au serveur doit contenir toutes les informations nécessaires pour la comprendre et y répondre. Le serveur ne conserve pas d'état de session entre les requêtes.
- Pas de session côté serveur
- Chaque requête est indépendante
- Nécessite généralement une authentification à chaque requête (JWT, API keys, etc.)
Opérations CRUD via les méthodes HTTP
Les API REST utilisent les méthodes HTTP standard pour effectuer les opérations CRUD (Create, Read, Update, Delete) sur les ressources.
- GET : Lecture de ressources
- POST : Création de ressources
- PUT : Mise à jour complète d'une ressource
- PATCH : Mise à jour partielle d'une ressource
- DELETE : Suppression d'une ressource
// Exemples d'endpoints REST pour une ressource "products"
// Récupérer la liste des produits
GET /api/products
// Récupérer un produit spécifique
GET /api/products/42
// Créer un nouveau produit
POST /api/products
Content-Type: application/json
{
"name": "Écouteurs sans fil Pro",
"description": "Écouteurs avec réduction active du bruit",
"price": 199.99,
"category_id": 8
}
// Mettre à jour un produit (complètement)
PUT /api/products/42
Content-Type: application/json
{
"name": "Écouteurs sans fil Pro+",
"description": "Écouteurs premium avec réduction active du bruit",
"price": 249.99,
"category_id": 8
}
// Mettre à jour partiellement un produit
PATCH /api/products/42
Content-Type: application/json
{
"price": 229.99
}
// Supprimer un produit
DELETE /api/products/42
// Récupérer les avis sur un produit (relation imbriquée)
GET /api/products/42/reviews
// Filtrer et paginer les résultats
GET /api/products?category=8&min_price=100&max_price=300&page=2&limit=20
// Trier les résultats
GET /api/products?sort=price_desc
Représentation des ressources
Les ressources sont représentées dans un format standard, généralement JSON ou XML. Le format JSON est aujourd'hui le plus utilisé pour sa légèreté et sa compatibilité avec JavaScript.
// Réponse JSON d'une API REST pour une requête GET /api/products/42
{
"id": 42,
"name": "Smartphone Galaxy X10",
"description": "Dernier modèle avec écran pliable et appareil photo 108MP",
"price": 999.99,
"currency": "EUR",
"stock": 157,
"category": {
"id": 8,
"name": "Électronique"
},
"tags": ["smartphone", "android", "premium"],
"rating": 4.7,
"created_at": "2023-05-12T10:30:00Z",
"updated_at": "2023-09-28T14:15:22Z",
"_links": {
"self": { "href": "/api/products/42" },
"category": { "href": "/api/categories/8" },
"reviews": { "href": "/api/products/42/reviews" }
}
}
Utilisation des codes HTTP
Les API REST utilisent les codes de statut HTTP pour indiquer le résultat d'une opération.
- 2xx : Succès (200 OK, 201 Created, 204 No Content)
- 4xx : Erreur client (400 Bad Request, 401 Unauthorized, 404 Not Found)
- 5xx : Erreur serveur (500 Internal Server Error, 503 Service Unavailable)
Implémentation avec
Node.js
Voici un exemple d'implémentation d'une API REST avec Node.js et Express, illustrant les bonnes pratiques:
// Exemple de REST API avec Node.js et Express
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
// Base de données simulée
let products = [
{ id: 1, name: 'Smartphone Galaxy X10', price: 999.99, category: 'Électronique' },
{ id: 2, name: 'Laptop Pro 15"', price: 1299.99, category: 'Informatique' }
];
// GET - Récupérer tous les produits
app.get('/api/products', (req, res) => {
// Filtrage
let result = [...products];
if (req.query.category) {
result = result.filter(p => p.category === req.query.category);
}
if (req.query.min_price) {
result = result.filter(p => p.price >= parseFloat(req.query.min_price));
}
// Pagination
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const startIndex = (page - 1) * limit;
// Ajouter des métadonnées de pagination
const totalItems = result.length;
const paginatedResult = {
data: result.slice(startIndex, startIndex + limit),
meta: {
total: totalItems,
page: page,
limit: limit,
total_pages: Math.ceil(totalItems / limit)
},
_links: {
self: `/api/products?page=${page}&limit=${limit}`,
first: '/api/products?page=1&limit=' + limit,
last: `/api/products?page=${Math.ceil(totalItems / limit)}&limit=${limit}`
}
};
// Ajouter next et prev si nécessaire
if (page > 1) {
paginatedResult._links.prev = `/api/products?page=${page - 1}&limit=${limit}`;
}
if (page < Math.ceil(totalItems / limit)) {
paginatedResult._links.next = `/api/products?page=${page + 1}&limit=${limit}`;
}
res.json(paginatedResult);
});
// GET - Récupérer un produit par son ID
app.get('/api/products/:id', (req, res) => {
const product = products.find(p => p.id === parseInt(req.params.id));
if (!product) {
return res.status(404).json({ error: 'Produit non trouvé' });
}
// Ajouter des liens HATEOAS
const productWithLinks = {
...product,
_links: {
self: `/api/products/${product.id}`,
collection: '/api/products'
}
};
res.json(productWithLinks);
});
// POST - Créer un nouveau produit
app.post('/api/products', (req, res) => {
const { name, price, category } = req.body;
// Validation basique
if (!name || !price) {
return res.status(400).json({ error: 'Le nom et le prix sont requis' });
}
const newProduct = {
id: products.length + 1,
name,
price,
category: category || 'Non catégorisé'
};
products.push(newProduct);
// Statut 201 Created avec l'en-tête Location
res.status(201)
.location(`/api/products/${newProduct.id}`)
.json(newProduct);
});
// PUT - Mettre à jour un produit
app.put('/api/products/:id', (req, res) => {
const productIndex = products.findIndex(p => p.id === parseInt(req.params.id));
if (productIndex === -1) {
return res.status(404).json({ error: 'Produit non trouvé' });
}
const { name, price, category } = req.body;
// Validation
if (!name || !price) {
return res.status(400).json({ error: 'Le nom et le prix sont requis' });
}
// Mise à jour complète
products[productIndex] = {
id: parseInt(req.params.id),
name,
price,
category: category || 'Non catégorisé'
};
res.json(products[productIndex]);
});
// DELETE - Supprimer un produit
app.delete('/api/products/:id', (req, res) => {
const productIndex = products.findIndex(p => p.id === parseInt(req.params.id));
if (productIndex === -1) {
return res.status(404).json({ error: 'Produit non trouvé' });
}
products.splice(productIndex, 1);
// Réponse 204 No Content
res.status(204).send();
});
// Démarrage du serveur
app.listen(3000, () => {
console.log('API REST démarrée sur le port 3000');
});
Documentation avec OpenAPI/Swagger
La documentation est cruciale pour les API REST. OpenAPI (anciennement Swagger) est devenu la norme de facto pour documenter les API REST de manière standardisée et interactive.
# Exemple de spécification OpenAPI (Swagger) pour une API REST
openapi: 3.0.3
info:
title: API de gestion de produits
description: API RESTful pour gérer un catalogue de produits
version: 1.0.0
contact:
name: API Support
email: support@example.com
url: https://www.example.com/support
servers:
- url: https://api.example.com/v1
description: Serveur de production
- url: https://staging-api.example.com/v1
description: Serveur de staging
paths:
/products:
get:
summary: Liste tous les produits
description: Retourne une liste paginée de produits avec support de filtrage
parameters:
- name: category
in: query
description: Filtre par catégorie
schema:
type: string
- name: min_price
in: query
description: Prix minimum
schema:
type: number
- name: page
in: query
description: Numéro de page pour la pagination
schema:
type: integer
default: 1
- name: limit
in: query
description: Nombre d'éléments par page
schema:
type: integer
default: 20
responses:
'200':
description: Liste des produits
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/Product'
meta:
$ref: '#/components/schemas/PaginationMeta'
post:
summary: Crée un nouveau produit
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ProductInput'
responses:
'201':
description: Produit créé avec succès
headers:
Location:
schema:
type: string
description: URL du nouveau produit
content:
application/json:
schema:
$ref: '#/components/schemas/Product'
'400':
description: Données invalides
/products/{id}:
get:
summary: Récupère un produit spécifique
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: Détails du produit
content:
application/json:
schema:
$ref: '#/components/schemas/Product'
'404':
description: Produit non trouvé
components:
schemas:
Product:
type: object
properties:
id:
type: integer
name:
type: string
price:
type: number
category:
type: string
_links:
type: object
properties:
self:
type: object
properties:
href:
type: string
collection:
type: object
properties:
href:
type: string
ProductInput:
type: object
required:
- name
- price
properties:
name:
type: string
minLength: 3
price:
type: number
minimum: 0
category:
type: string
PaginationMeta:
type: object
properties:
total:
type: integer
page:
type: integer
limit:
type: integer
total_pages:
type: integer
Bonnes pratiques
- Noms au pluriel pour les collections - Utilisez /products plutôt que /product
- Utilisation de noms et non de verbes - /products plutôt que /getProducts
- Versioning de l'API - /v1/products pour permettre des évolutions futures
- Pagination - Pour les collections qui peuvent contenir un grand nombre d'éléments
- Filtrage, tri et recherche - Via des paramètres de requête
- HATEOAS (Hypertext As The Engine Of Application State) - Inclusion de liens dans les réponses pour faciliter la navigation
- Gestion des erreurs cohérente - Format standard pour les messages d'erreur
Cas d'usage
Applications mobiles
Les API REST sont idéales pour fournir des données aux applications mobiles, offrant une communication efficace entre le client mobile et le serveur.
Architectures microservices
Les API REST permettent la communication entre les différents microservices d'une architecture distribuée, assurant l'indépendance et la modularité.
Intégrations tierces
Les API REST facilitent l'intégration avec des services tiers et des partenaires, permettant l'échange de données de manière standardisée.
Applications web modernes (SPA)
Les Single Page Applications (React, Vue, Angular) utilisent des API REST pour récupérer et modifier les données sans recharger la page.
APIs REST publiques populaires
De nombreuses entreprises exposent leurs données et fonctionnalités via des API REST: