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
ContrainteSignification
Client-ServeurSéparation des responsabilités (front ≠ back)
Sans état (Stateless)Chaque requête contient toute l'info nécessaire (pas de session serveur)
CacheLes réponses peuvent être mises en cache (headers Cache-Control)
Interface uniformeURLs cohérentes, méthodes HTTP standard, JSON
Système en couchesLoad 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éthodeActionIdempotentBodyExemple
GETLire✅ OuiNonRécupérer un utilisateur
POSTCréer❌ NonOuiCréer un utilisateur
PUTRemplacer (tout)✅ OuiOuiRemplacer toutes les infos
PATCHModifier (partiel)❌ NonOuiModifier juste le nom
DELETESupprimer✅ OuiNonSupprimer 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
CodeNomQuand l'utiliser
200OKGET réussi, PATCH réussi
201CreatedPOST réussi (ressource créée)
204No ContentDELETE réussi (pas de body)
301Moved PermanentlyURL changée définitivement
304Not ModifiedLe cache est encore valide
400Bad RequestDonnées invalides (validation)
401UnauthorizedPas authentifié (token manquant/invalide)
403ForbiddenAuthentifié mais pas autorisé (pas les droits)
404Not FoundRessource inexistante
409ConflictConflit (email déjà pris, doublon)
422Unprocessable EntitySyntaxe OK mais sémantique invalide
429Too Many RequestsRate limit dépassé
500Internal Server ErrorBug serveur (ne jamais exposer le détail)
503Service UnavailableServeur 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éthodeIdéal pourStockage
JWT (Bearer Token)APIs stateless, mobile, SPAClient (localStorage/cookie)
Session + CookieApps web server-side, MPAServeur (Redis/DB)
API KeyService-to-service, APIs publiquesHeader 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éé

OutilRôle
Swagger UIInterface interactive pour tester l'API (/docs)
RedocDocumentation lisible et élégante (/redoc)
PostmanClient HTTP pour tester manuellement
InsomniaAlternative à Postman (plus léger)
BrunoClient 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
MenaceProtection
Injection SQL/NoSQLORM (Prisma, Sequelize, SQLAlchemy) + validation input
XSSÉchapper les outputs, Content-Security-Policy
Brute forceRate limiting, captcha, lockout après N échecs
CSRFCookie SameSite, CSRF token pour les formulaires
Man-in-the-middleHTTPS obligatoire, HSTS
Données sensibles exposéesNe 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
AspectRESTGraphQLgRPC
FormatJSONJSON (query flexible)Protocol Buffers (binaire)
TransportHTTPHTTP (POST unique)HTTP/2
Sur-fetchingPossible (retourne tout)Non (le client choisit)Non (schema strict)
N+1 requêtesPossibleRésolu (1 requête)N/A
PerformanceBonneBonneExcellente (binaire)
CourbeSimpleMoyenneComplexe
Idéal pourCRUD, APIs publiquesApps complexes, mobileMicroservices 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