API REST Design
Problème
Les APIs sont conçues sans conventions cohérentes : verbes HTTP mal utilisés, pas de versioning, erreurs non standardisées, pagination absente.
Solution
Conventions REST + versioning + error handling standardisé + documentation OpenAPI.
1. Conventions REST
Verbes HTTP
| Verbe | Usage | Exemple |
|---|---|---|
GET |
Lecture, jamais de modification | GET /api/v1/users |
POST |
Création | POST /api/v1/users |
PUT |
Remplacement complet | PUT /api/v1/users/123 |
PATCH |
Modification partielle | PATCH /api/v1/users/123 |
DELETE |
Suppression | DELETE /api/v1/users/123 |
Naming
- Resources au pluriel :
/users,/orders,/products - Nesting max 2 niveaux :
/users/123/orders(OK),/users/123/orders/456/items/789(trop) - kebab-case :
/order-items, pas/orderItemsni/order_items - Filtres en query params :
GET /users?role=admin&status=active
Status codes
| Code | Usage |
|---|---|
| 200 | OK (GET, PATCH, PUT) |
| 201 | Created (POST) |
| 204 | No content (DELETE) |
| 400 | Bad request (validation) |
| 401 | Unauthorized (pas authentifié) |
| 403 | Forbidden (authentifié mais pas les droits) |
| 404 | Not found |
| 409 | Conflict (duplicate) |
| 422 | Unprocessable entity (validation métier) |
| 429 | Too many requests (rate limit) |
| 500 | Server error |
2. Versioning
# URL versioning (le plus simple, le plus courant)
GET /api/v1/users
GET /api/v2/users
# Header versioning (alternative)
Accept: application/vnd.myapi.v2+json
- v1 dès le départ — même si une seule version
- Déprécation : header
Sunset: Sat, 01 Jan 2027 00:00:00 GMT - Pas de breaking change sans nouvelle version majeure
3. Pagination
// Cursor-based (recommandé pour grandes datasets)
// Request: GET /api/v1/users?cursor=eyJpZCI6MTIzfQ&limit=20
// Response:
{
"data": [...],
"pagination": {
"hasMore": true,
"nextCursor": "eyJpZCI6MTQzfQ"
}
}
// Offset-based (simple, OK pour petites datasets)
// GET /api/v1/users?page=3&limit=20
// Response:
{
"data": [...],
"pagination": {
"page": 3,
"limit": 20,
"total": 500,
"totalPages": 25
}
}
Cursor > Offset car : stable si données insérées, performant sur grandes tables.
4. Error handling standardisé
// Format d'erreur unique
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Email is required",
"details": [
{ "field": "email", "message": "required" }
],
"requestId": "req_abc123"
}
}
// Express middleware
app.use((err, req, res, next) => {
const status = err.status || 500;
res.status(status).json({
error: {
code: err.code || 'INTERNAL_ERROR',
message: err.message,
details: err.details,
requestId: req.id
}
});
});
5. OpenAPI / Swagger
# openapi.yaml — documentation + validation
openapi: 3.1.0
info:
title: My API
version: 1.0.0
paths:
/api/v1/users:
get:
summary: List users
parameters:
- name: limit
in: query
schema: { type: integer, minimum: 1, maximum: 100, default: 20 }
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/UserList'
- Générer depuis le code (tsoa, zod-to-openapi, drizzle-zod)
- Valider les requêtes contre le schema OpenAPI
- Exposer
/api/docsavec Swagger UI ou Scalar
6. Rate limiting
// Par utilisateur + par IP
const limiter = rateLimit({
windowMs: 60 * 1000, // 1 min
max: (req) => req.user?.tier === 'premium' ? 100 : 20,
keyGenerator: (req) => req.user?.id || req.ip,
handler: (req, res) => {
res.set('Retry-After', '60');
res.status(429).json({
error: { code: 'RATE_LIMITED', message: 'Too many requests' }
});
}
});
Anti-patterns
GETqui modifie des données- Verbes dans l'URL :
/api/v1/getUsers,/api/v1/createOrder - Pas de pagination sur les listes
- 200 avec
{ "error": "..." }dans le body - Pas de versioning
- Error messages qui leak des infos internes (stack traces, SQL)
Références
- [[KNOW-PAT-220]] — Secure SDLC Checklist
- [[KNOW-PAT-224]] — Secure Auth (rate limiting, sessions)
- [[KNOW-PAT-223]] — Security Headers & CSP
- [[KNOW-REF-008]] — roadmap.sh Backend