APIs & Services
REST, GraphQL, gRPC, contrats, versioning, documentation & tests : tout ce que le back-end dev doit maĂźtriser pour exposer des services propres, stables et observables.
REST, GraphQL, gRPC, events Contrats, versioning, compat ascendante Auth, scopes, rate limiting & tests dâAPI
1) Fundamentals HTTP & REST
Rappels HTTP
- Verbes : GET (lecture), POST (création), PUT/PATCH (MAJ), DELETE (suppression).
- Statuts : 2xx succĂšs, 4xx erreur client, 5xx erreur serveur.
- Idempotence : GET, PUT, DELETE doivent pouvoir ĂȘtre rejouĂ©s sans effet secondaire.
GET /orders/123 -> 200 OK
POST /orders -> 201 Created
DELETE /orders/123 -> 204 No ContentREST (simplifié)
- Ressources adressables par URI :
/orders/123. - ReprĂ©sentations (JSON, XMLâŠ) + liens Ă©ventuellement (HATEOAS light).
- Sans état cÎté serveur (stateless) pour la scalabilité.
GET /orders/123
{
"id": "123",
"status": "paid",
"_links": {
"cancel": { "href": "/orders/123/cancel" }
}
}Exemple minimal FastAPI
from fastapi import FastAPI
from pydantic import BaseModel
class OrderIn(BaseModel):
amount: float
app = FastAPI()
@app.post("/orders", status_code=201)
def create_order(payload: OrderIn):
# TODO: persister...
return {"id": "ord_123", "amount": payload.amount}2) Design dâAPI REST : bonnes pratiques
Ressources & naming
- Pluriel en snake/kebab :
/users,/user-groups. - Relations :
/users/{id}/orders. - Actions âverbesâ â sub-ressources :
POST /orders/{id}/cancel.
GET /orders?status=paid&page=2
POST /orders/{id}/cancelFiltres, tri, pagination
- Filtres via query params :
?status=paid&from=2025-01-01. - Tri :
?sort=-created_at. - Pagination :
?page=2&page_size=50(ou offset/limit).
GET /orders?page=1&page_size=20
{
"items": [...],
"page": 1,
"page_size": 20,
"total": 142
}Erreurs & codes
- Validation â 400, Auth â 401, droits â 403, non trouvĂ© â 404.
- Inclure un code dâerreur applicatif + message.
HTTP/1.1 422 Unprocessable Entity
{
"error": "INVALID_STATUS",
"message": "Status must be one of: draft, paid, cancelled"
}Checklist design REST
- Endpoints stables & documentĂ©s, pas de â/doStuffâ obscur.
- Contrats de rĂ©ponse cohĂ©rents (mĂȘme structure partout).
- Pas de payload sur GET, Ă©viter les âRPC HTTPâ dĂ©guisĂ©s.
3) GraphQL & gRPC : quand les utiliser ?
GraphQL
- Client choisit exactement les champs quâil veut.
- Un seul endpoint, schéma typé.
- TrĂšs pratique pour front-end (mobile, SPA).
type Query {
order(id: ID!): Order
}
type Order {
id: ID!
status: String!
total: Float!
}query {
order(id: "123") {
id
total
}
}gRPC
- Protobuf + HTTP/2, trĂšs efficace inter-services.
- Streaming bi-directionnel possible.
- Contrats forts; clients générés auto.
syntax = "proto3";
service Orders {
rpc GetOrder(GetOrderRequest) returns (OrderResponse);
}
message GetOrderRequest { string id = 1; }
message OrderResponse { string id = 1; string status = 2; }Choisir quoi ?
- REST : par défaut, surtout B2B / intégrations publiques.
- GraphQL : besoin fort de flexibilité cÎté front.
- gRPC : microservices internes haute perf, streaming.
4) Contrats, versioning & compatibilité
OpenAPI (Swagger)
- Source de vérité du contrat REST.
- Génération doc & SDK clients.
- Base pour tests automatisés.
paths:
/orders/{id}:
get:
summary: Get order by id
parameters:
- in: path
name: id
schema: { type: string }
responses:
"200": { description: OK }Versioning
- Version dans lâURL :
/api/v1/orders. - Compat ascendante : ajouter des champs, ne pas en supprimer.
- Deprecation header + doc : annoncer la fin dâun endpoint.
Breaking changes & contrats
- Changer un type ou supprimer un champ = breaking.
- Utiliser tests de contrat (Pact, etc.).
- Prévoir une période de cohabitation v1/v2.
{
"field": "status",
"breaking_change": "enum reduced",
"mitigation": "introduce new field 'lifecycle_status'"
}5) Sécurité & limites des APIs
Auth / Authz
- JWT / OAuth2 / API keys selon cas.
- Scopes & rĂŽles :
orders:read,orders:write. - Rotation des clés & tokens courts + refresh.
Authorization: Bearer <access_token>Rate limiting & quotas
- Limiter par IP, client_id, user_id.
- Retourner des infos dans les headers :
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
Retry-After: 60Sécurité applicative
- Validation stricte des entrées (types, tailles, patterns).
- Ne pas exposer les stack traces dans les réponses.
- Audit log pour les actions sensibles (login, suppression, etc.).
6) Documentation & Developer Experience (DX)
Docs essentielles
- Guide âGetting startedâ en 5â10 minutes.
- Exemples dâappels (curl, Python, JS, Postman).
- Politique dâauth, quotas, erreurs typiques.
Exemple dâappel curl
curl -X POST https://api.example.com/v1/orders \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"amount": 123.45}'Snippet client Python
import requests
r = requests.post(
"https://api.example.com/v1/orders",
headers={"Authorization": f"Bearer {token}"},
json={"amount": 123.45},
)
print(r.status_code, r.json())DX cÎté équipe interne
- Environnements âsandboxâ pour tests.
- Postman collections / Insomnia exportées.
- Exemples dâerreurs frĂ©quentes et comment les corriger.
7) Tests dâAPI : de lâunitaire au load test
Tests unitaires & intégration
- Tester la logique métier sans HTTP (services).
- Tests dâAPI avec client HTTP in-process (ex :
TestClientFastAPI).
def test_create_order(client):
r = client.post("/orders", json={"amount": 10})
assert r.status_code == 201Tests de contrat (consumer-driven)
- Producteur & consommateur partagent un contrat.
- Si on casse le contrat, la CI échoue.
{
"consumer": "frontend-app",
"provider": "orders-api",
"interaction": {
"request": { "method": "GET", "path": "/orders/123" },
"response": { "status": 200, "body": { "id": "123" } }
}
}Tests de charge & smoke tests
- Locust, k6, JMeter⊠pour simuler le trafic.
- Smoke test aprÚs déploiement : quelques appels critiques.
# Exemple Locust (Python)
class OrdersUser(HttpUser):
@task
def get_order(self):
self.client.get("/orders/123")REST, GraphQL, gRPC & Ă©vĂ©nements â Vue dâensemble
| Style | Transport / format | Forces | Cas dâusage typiques |
|---|---|---|---|
| REST | HTTP + JSON | Simple, universel, facilement testable. | APIs publiques, B2B, exposer des ressources métier. |
| GraphQL | HTTP + schéma typé | Le client choisit les champs, peu de sur/under-fetching. | Front web/mobile avec besoins complexes de données. |
| gRPC | HTTP/2 + Protobuf | TrÚs performant, contrats forts, génération de clients. | Microservices internes, streaming, faible latence. |
| Events | MQ / log dâĂ©vĂ©nements (Kafka, RabbitMQâŠ) | DĂ©couplage fort, asynchrone, scalabilitĂ©. | IntĂ©grations, side-effects, synchronisation de donnĂ©es. |
Exemple REST (FastAPI)
@app.get("/orders/{id}")
def get_order(id: str):
return {"id": id, "status": "paid"}Exemple gRPC (extrait .proto)
service Orders {
rpc GetOrder(GetOrderRequest) returns (OrderResponse);
}
message GetOrderRequest { string id = 1; }
message OrderResponse { string id = 1; string status = 2; }Contrats dâAPI, versioning & compatibilitĂ© ascendante
Contrat = source de vérité
- Spécification OpenAPI, schema GraphQL ou proto gRPC.
- Versionné dans Git, validé en CI.
- Point de départ pour la doc & les SDK.
paths:
/orders/{id}:
get:
operationId: getOrder
responses:
"200":
description: OKVersioning
- URL versionnée :
/api/v1/...,/api/v2/.... - Compat ascendante : on peut ajouter des champs, pas en supprimer.
- Deprecation header + date dâexpiration annoncĂ©e.
Deprecation: true
Sunset: Wed, 31 Dec 2025 23:59:59 GMTBreaking / non-breaking
- Changer un type (string â int) = breaking.
- Rendre un champ obligatoire = breaking.
- Ajouter un champ optionnel = OK (non-breaking).
{
"change": "field 'status' enum extended",
"impact": "non-breaking for tolerant clients"
}Mini-checklist âcontrat APIâ
- Contrat versionné, validé par lint / CI ?
- Stratégie écrite pour déprécier/remplacer un endpoint ?
- Tests de contrat (consumer/provider) en place pour éviter les régressions ?
Auth, scopes, rate limiting & tests dâAPI
Auth & scopes
- JWT / OAuth2 : access token court, refresh token plus long.
- Scopes fonctionnels :
orders:read,orders:write. - RÎles & permissions cÎté service (RBAC / ABAC).
Authorization: Bearer <access_token>Rate limiting & protections
- Limiter par clé API / user / IP.
- Réponse claire en cas de 429 :
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
Retry-After: 60- Protection contre brute force, input validation stricte.
Tests dâAPI Ă couvrir
- Happy path pour chaque endpoint.
- Erreurs : auth manquante, droits insuffisants, payload invalide.
- Tests de charge sur endpoints critiques (Locust, k6âŠ).
def test_unauthorized(client):
r = client.get("/orders/123")
assert r.status_code == 4018) Anti-patterns dâAPI & checklist finale
Anti-patterns fréquents
- API âchattyâ : 10 appels pour charger une page â penser agrĂ©gation / BFF.
- Utiliser POST pour tout (mĂȘme des lectures).
- Coupler le modĂšle dâAPI 1:1 au modĂšle de DB interne.
- Changer les réponses sans préavis (breaking changes silencieux).
- Absence dâauth ou mĂȘme clĂ© identique pour tous les clients.
Checklist de design dâAPI (extrait)
[
"Endpoints nommés de maniÚre cohérente ?",
"Verbes HTTP & statuts respectés ?",
"Payloads validés & typés (schemas) ?",
"OpenAPI à jour & publié ?",
"Auth + scopes définis ?",
"Rate limiting & quotas configurés ?",
"Logs structurés avec trace-id ?",
"Tests dâAPI dans la CI ?"
]