Cours APIs REST Complet 🔌

Les 12 chapitres essentiels — HTTP, CRUD, authentification, pagination et documentation

12
Chapitres
100+
Exemples
Multi-lang
Node, Python, PHP
A1→C2
Niveaux

CHAPITRE 01

Introduction : qu'est-ce qu'une API REST ?

🔌 API = interface entre deux logiciels
# Exemple concret : votre app mobile commande un café

# 1. Le client (app mobile) envoie une REQUÊTE
POST /api/orders

# 2. Le serveur traite la requête et renvoie une RÉPONSE
201 Created

API (Application Programming Interface) = un contrat entre deux logiciels. REST (Representational State Transfer) = un style d'architecture pour les APIs web, défini par Roy Fielding en 2000. Le client et le serveur communiquent via HTTP en échangeant du JSON. Chaque URL représente une ressource (utilisateur, produit, commande). Les actions sont définies par les méthodes HTTP (GET, POST, PUT, DELETE).

🏗️ Les 6 contraintes REST
Contrainte Signification
Client-Serveur Séparation des responsabilités (front ≠ back)
Sans état (Stateless) Chaque requête contient toute l'info nécessaire (pas de session serveur)
Cache Les réponses peuvent être mises en cache (headers Cache-Control)
Interface uniforme URLs cohérentes, méthodes HTTP standard, JSON
Système en couches Load balancer, cache, gateway — transparent pour le client
Code à la demande (Optionnel) Le serveur peut envoyer du code exécutable

CHAPITRE 02

Le protocole HTTP

📡 Anatomie d'une requête HTTP
# REQUÊTE
GET /api/users/42 HTTP/1.1 ← Méthode + URL + Version
Host: api.example.com ← Headers
Authorization: Bearer eyJhbGc…
Accept: application/json
Content-Type: application/json

# RÉPONSE
HTTP/1.1 200 OK ← Status code + Message
Content-Type: application/json ← Headers
Cache-Control: max-age=3600
X-Total-Count: 150

🔧 Méthodes HTTP
Méthode Action Idempotent Body Exemple
GET Lire ✅ Oui Non Récupérer un utilisateur
POST Créer ❌ Non Oui Créer un utilisateur
PUT Remplacer (tout) ✅ Oui Oui Remplacer toutes les infos
PATCH Modifier (partiel) ❌ Non Oui Modifier juste le nom
DELETE Supprimer ✅ Oui Non Supprimer un utilisateur

Idempotent = même résultat si on répète la requête. GET /users/42 retourne toujours le même utilisateur. DELETE /users/42 supprime une fois — les appels suivants ne changent rien. POST /users crée un nouvel utilisateur à chaque appel — c'est pour ça qu'il n'est pas idempotent.

CHAPITRE 03

Conception d'une API REST

🏗️ Nommer les URLs (endpoints)
# ✅ Bonnes URLs
GET /api/users # Liste des utilisateurs
GET /api/users/42 # Un utilisateur
POST /api/users # Créer un utilisateur
PUT /api/users/42 # Remplacer l'utilisateur 42
PATCH /api/users/42 # Modifier partiellement
DELETE /api/users/42 # Supprimer l'utilisateur 42

# Ressources imbriquées
GET /api/users/42/posts # Posts de l'utilisateur 42
POST /api/users/42/posts # Créer un post pour l'utilisateur 42
GET /api/posts/7/comments # Commentaires du post 7

# Filtres et recherche (query parameters)
GET /api/users?role=admin&active=true
GET /api/posts?author=42&sort=-created_at
GET /api/products?q=laptop&min_price=500&max_price=2000

# Pagination
GET /api/posts?page=2&limit=20

❌ Mauvaises URLs : GET /getUsers, POST /createUser, DELETE /deleteUser/42. L'action est dans la méthode HTTP, pas dans l'URL. L'URL représente une ressource (nom, pluriel). Toujours en minuscules, kebab-case si nécessaire : /api/blog-posts.

📋 Règles de conception
Règle ✅ Correct ❌ Incorrect
Noms, pas de verbes /api/users /api/getUsers
Pluriel /api/products /api/product
Minuscules + kebab-case /api/blog-posts /api/BlogPosts
Pas de trailing slash /api/users /api/users/
Préfixe versionné /api/v1/users /users
Filtres en query params ?role=admin /api/users/admin

CHAPITRE 04

CRUD en pratique (Node.js)

🟢 API Express.js complète
// npm init -y && npm install express
const express = require('express');
const app = express();
app.use(express.json()); // Parser le body JSON

let users = [
,
,
];

// GET /api/users — Liste
app.get('/api/users', (req, res) => );

// GET /api/users/:id — Détail
app.get('/api/users/:id', (req, res) => );

// POST /api/users — Créer
app.post('/api/users', (req, res) => );

// PATCH /api/users/:id — Modifier
app.patch('/api/users/:id', (req, res) => );

// DELETE /api/users/:id — Supprimer
app.delete('/api/users/:id', (req, res) => );

app.listen(3000, () => console.log('API sur http://localhost:3000'));

CHAPITRE 05

CRUD en pratique (Python)

🐍 API FastAPI complète
# pip install fastapi uvicorn
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, EmailStr

app = FastAPI(title=« Mon API », version=« 1.0 »)

# Schéma de validation (Pydantic)
class UserCreate(BaseModel):
name: str
email: EmailStr

class User(UserCreate):
id: int

users_db: list[User] = [
User(id=1, name=« Alice », email=« alice@mail.com »),
User(id=2, name=« Bob », email=« bob@mail.com »),
]

@app.get(« /api/users », response_model=list[User])
def list_users():
return users_db

@app.get(« /api/users/ », response_model=User)
def get_user(user_id: int):
user = next((u for u in users_db if u.id == user_id), None)
if not user:
raise HTTPException(status_code=404, detail=« User not found »)
return user

@app.post(« /api/users », response_model=User, status_code=201)
def create_user(data: UserCreate):
user = User(id=len(users_db) + 1, **data.model_dump())
users_db.append(user)
return user

@app.delete(« /api/users/ », status_code=204)
def delete_user(user_id: int):
user = next((u for u in users_db if u.id == user_id), None)
if not user:
raise HTTPException(status_code=404, detail=« User not found »)
users_db.remove(user)

# Lancer : uvicorn main:app –reload
# Doc auto : http://localhost:8000/docs (Swagger UI)

FastAPI génère automatiquement la documentation Swagger à partir de vos types Pydantic et de vos routes. Accédez à /docs pour une interface interactive, et /redoc pour une doc plus lisible. La validation du body, des query params et des path params est automatique grâce au typage Python.

CHAPITRE 06

Codes de statut HTTP

📊 Les codes essentiels
Code Nom Quand l'utiliser
200 OK GET réussi, PATCH réussi
201 Created POST réussi (ressource créée)
204 No Content DELETE réussi (pas de body)
301 Moved Permanently URL changée définitivement
304 Not Modified Le cache est encore valide
400 Bad Request Données invalides (validation)
401 Unauthorized Pas authentifié (token manquant/invalide)
403 Forbidden Authentifié mais pas autorisé (pas les droits)
404 Not Found Ressource inexistante
409 Conflict Conflit (email déjà pris, doublon)
422 Unprocessable Entity Syntaxe OK mais sémantique invalide
429 Too Many Requests Rate limit dépassé
500 Internal Server Error Bug serveur (ne jamais exposer le détail)
503 Service Unavailable Serveur en maintenance

401 vs 403 : 401 = « Qui es-tu ? » (pas de token, ou token expiré). 403 = « Je sais qui tu es, mais tu n'as pas le droit » (rôle insuffisant). Un utilisateur normal qui tente d'accéder à /api/admin → 403, pas 401.

CHAPITRE 07

Validation et erreurs

🛡️ Format d'erreur standardisé
// Format d'erreur cohérent (RFC 7807 — Problem Details)

// 404

// 500 — JAMAIS exposer le stack trace au client

Validez TOUJOURS l'input côté serveur, même si le client valide aussi. Bibliothèques recommandées : Zod (Node.js/TypeScript), Pydantic (Python/FastAPI), Joi (Node.js). Ne faites jamais confiance aux données du client — validez le type, la longueur, le format, et assainissez pour éviter les injections.

CHAPITRE 08

Authentification

🔐 JWT (JSON Web Token)
// 1. L'utilisateur se connecte
POST /api/auth/login

// 2. Le serveur vérifie et renvoie un token JWT
200 OK

// 3. Le client envoie le token dans chaque requête
GET /api/users/me
Authorization: Bearer eyJhbGciOiJIUzI1NiIs…

// Structure d'un JWT (3 parties séparées par des .)
// HEADER.PAYLOAD.SIGNATURE
// Header :
// Payload :
// Signature : HMACSHA256(header + payload, SECRET_KEY)

⚖️ Méthodes d'authentification
Méthode Idéal pour Stockage
JWT (Bearer Token) APIs stateless, mobile, SPA Client (localStorage/cookie)
Session + Cookie Apps web server-side, MPA Serveur (Redis/DB)
API Key Service-to-service, APIs publiques Header X-API-Key
OAuth 2.0 « Connexion avec Google/GitHub » Provider externe

Ne stockez JAMAIS un JWT dans localStorage pour une app web (vulnérable aux attaques XSS). Préférez un cookie httpOnly + secure + sameSite. Pour les apps mobiles, utilisez le secure storage natif (Keychain iOS, EncryptedSharedPreferences Android).

CHAPITRE 09

Pagination, tri et filtrage

📄 Pagination offset vs cursor
// 1. Pagination par OFFSET (simple, pour les petits datasets)
GET /api/posts?page=2&limit=20

// Réponse

// 2. Pagination par CURSOR (performante, pour les gros datasets)
GET /api/posts?limit=20&after=eyJpZCI6NDJ9

// Réponse

🔍 Tri et filtrage
// Tri (- = décroissant)
GET /api/posts?sort=-created_at # Plus récents d'abord
GET /api/posts?sort=price,-rating # Prix croissant, note décroissante

// Filtrage
GET /api/products?category=electronics&in_stock=true
GET /api/products?min_price=100&max_price=500

// Recherche
GET /api/products?q=laptop

// Sélection de champs (sparse fieldsets)
GET /api/users?fields=id,name,email # Retourne seulement ces champs

// Include (relations)
GET /api/posts?include=author,comments # Embarquer les relations

CHAPITRE 10

Documentation (OpenAPI)

📚 OpenAPI / Swagger
# openapi.yaml (spécification OpenAPI 3.0)
openapi: « 3.0.0 »
info:
title: Mon API
version: « 1.0 »
description: API de gestion des utilisateurs

paths:
/api/users:
get:
summary: Liste des utilisateurs
parameters:
name: page
in: query
schema:
name: limit
in: query
schema:
responses:
« 200 »:
description: Liste paginée
post:
summary: Créer un utilisateur
requestBody:
required: true
content:
application/json:
schema:
$ref: « #/components/schemas/UserCreate »
responses:
« 201 »:
description: Utilisateur créé

Outil Rôle
Swagger UI Interface interactive pour tester l'API (/docs)
Redoc Documentation lisible et élégante (/redoc)
Postman Client HTTP pour tester manuellement
Insomnia Alternative à Postman (plus léger)
Bruno Client API open-source (fichiers Git-friendly)

CHAPITRE 11

Sécurité

🔒 Sécuriser une API
// 1. HTTPS obligatoire (jamais HTTP en production)

// 2. Rate limiting — limiter les requêtes par IP/utilisateur
// Express : npm install express-rate-limit
const rateLimit = require('express-rate-limit');
app.use(rateLimit());

// 3. CORS — contrôler les origines autorisées
// Express : npm install cors
const cors = require('cors');
app.use(cors());

// 4. Helmet — headers de sécurité HTTP
// Express : npm install helmet
const helmet = require('helmet');
app.use(helmet());

// 5. Validation input (Zod)
// Ne jamais insérer de l'input utilisateur brut en SQL/NoSQL

// 6. Ne jamais exposer les erreurs internes
// ❌ res.status(500).json()
// ✅ res.status(500).json()

🛡️ Checklist sécurité API
Menace Protection
Injection SQL/NoSQL ORM (Prisma, Sequelize, SQLAlchemy) + validation input
XSS Échapper les outputs, Content-Security-Policy
Brute force Rate limiting, captcha, lockout après N échecs
CSRF Cookie SameSite, CSRF token pour les formulaires
Man-in-the-middle HTTPS obligatoire, HSTS
Données sensibles exposées Ne jamais retourner le mot de passe, même hashé
Accès non autorisé Vérifier les permissions sur chaque endpoint

CHAPITRE 12

Bonnes pratiques

⚖️ REST vs GraphQL vs gRPC
Aspect REST GraphQL gRPC
Format JSON JSON (query flexible) Protocol Buffers (binaire)
Transport HTTP HTTP (POST unique) HTTP/2
Sur-fetching Possible (retourne tout) Non (le client choisit) Non (schema strict)
N+1 requêtes Possible Résolu (1 requête) N/A
Performance Bonne Bonne Excellente (binaire)
Courbe Simple Moyenne Complexe
Idéal pour CRUD, APIs publiques Apps complexes, mobile Microservices internes
✅ Bonnes pratiques

✅ À FAIRE
• Noms de ressources au pluriel
• Codes de statut HTTP corrects
• Validation de tout input
• Pagination sur les listes
• Versionner l'API (/api/v1/)
• Format d'erreur cohérent
• HTTPS obligatoire
• Rate limiting
• Documenter avec OpenAPI
• CORS configuré correctement

❌ À ÉVITER
• Verbes dans les URLs (/getUsers)
• Retourner 200 pour les erreurs
• Exposer les stack traces au client
• Pas de pagination (listes infinies)
• Stocker JWT dans localStorage (web)
• Retourner les mots de passe
• SQL brut avec l'input utilisateur
• Ignorer CORS en prod
• API sans authentification ni rate limit
• Pas de documentation

🧠 Quiz
PUT vs PATCH — quelle différence ?
PUT remplace entièrement la ressource — vous devez envoyer tous les champs. Si vous oubliez un champ, il sera supprimé. PATCH modifie partiellement — envoyez seulement les champs à modifier. En pratique, PATCH est plus courant car on modifie rarement toute une ressource d'un coup.
Pourquoi une API doit être stateless ?
Stateless = le serveur ne garde aucune info entre les requêtes. Chaque requête contient tout ce qu'il faut (token JWT, query params). Avantages : scaling horizontal (n'importe quel serveur peut répondre), pas de session à partager entre serveurs, simplicité (pas de synchronisation d'état). Le state est dans le token (JWT) ou en BDD, pas en mémoire serveur.
Pagination offset vs cursor — quand choisir quoi ?
Offset (?page=3&limit=20) = simple, permet de sauter à la page 50, mais lent sur les gros datasets (OFFSET 10000 en SQL scanne tout). Cursor (?after=abc123) = performant même sur des millions de lignes (WHERE id > cursor), mais pas de saut de page. Pour < 100k lignes → offset. Pour plus → cursor.

Cours APIs REST Complet — HTTP, CRUD, authentification, sécurité et documentation

Référence : OpenAPI Spec | MDN HTTP | FastAPI