02 - Des URLs qui ont du sens
Ce que tu vas apprendre
- Pourquoi les URLs REST utilisent des noms et pas des verbes
- Les conventions de nommage : pluriel, kebab-case, hiérarchie
- Comment structurer les query params pour le filtrage
- Des exemples concrets tires de vraies API
Prerequisites
- Avoir lu l'article sur les principes REST
- Connaitre les bases du protocole HTTP
L'URL, c'est ton contrat
J'ai bosse sur une API ou les endpoints ressemblaient a ca : /api/getUserById, /api/createNewOrder, /api/deleteProductFromCart. Le dev qui l'avait écrite etait parti depuis six mois. Personne ne savait si deleteProductFromCart faisait un soft delete ou un hard delete. Et personne n'osait regarder.
Une bonne URL REST, c'est comme une bonne adresse postale : tu sais ce que tu vas trouver avant d'ouvrir la porte. L'URL identifié une ressource. La méthode HTTP dit ce que tu veux en faire. Melanger les deux, c'est le début des problèmes.
Des noms, pas des verbes
La regle numero un : tes URLs representent des ressources (des noms), pas des actions (des verbes). L'action, c'est la méthode HTTP qui la porte.
http# Mauvais : verbes dans l'URL
GET /api/getUsers
POST /api/createUser
POST /api/deleteUser/42
GET /api/fetchOrdersByUser/42
# Bon : noms au pluriel + methodes HTTP
GET /api/users # lister les utilisateurs
POST /api/users # creer un utilisateur
DELETE /api/users/42 # supprimer un utilisateur
GET /api/users/42/orders # commandes d'un utilisateur
La seule exception acceptable, c'est quand tu modèles une action qui n'est pas du CRUD. Par exemple /api/users/42/activate ou /api/orders/99/cancel. Certains puristes n'aiment pas, mais c'est pragmatique et lisible.
Toujours au pluriel
Utilise le pluriel pour tes collections, meme quand tu accedes a un seul élément.
httpGET /api/users # la collection
GET /api/users/42 # un element de la collection
Pourquoi pas le singulier pour un élément ? Parce que /api/user/42 et /api/users creent une inconsistance. Tu dois retenir deux formes. Avec le pluriel partout, c'est previsible.
J'ai vu un projet ou certains endpoints etaient au singulier et d'autres au pluriel. Le frontend avait des constantes USER_ENDPOINT et USERS_ENDPOINT partout. Un cauchemar de maintenance.
Hiérarchie et relations
Les URLs REST sont hierarchiques. Elles refletent les relations entre ressources.
http# Un utilisateur
GET /api/users/42
# Les commandes de cet utilisateur
GET /api/users/42/orders
# Une commande specifique de cet utilisateur
GET /api/users/42/orders/99
# Les articles de cette commande
GET /api/users/42/orders/99/items
En TypeScript, ca donne un router Express assez lisible :
typescriptconst router = express.Router();
router.get("/api/users/:userId/orders", async (req, res) => {
const { userId } = req.params;
const orders = await orderService.findByUser(userId);
res.json(orders);
});
router.get("/api/users/:userId/orders/:orderId", async (req, res) => {
const { userId, orderId } = req.params;
const order = await orderService.findOne(userId, orderId);
if (!order) {
return res.status(404).json({ error: "Order not found" });
}
res.json(order);
});
Ne dépassé pas 3 niveaux de profondeur. /api/users/42/orders/99/items/5/reviews c'est trop. A ce stade, fais un endpoint /api/reviews?itemId=5 a la place.
Kebab-case pour les URLs
http# Bon : kebab-case
GET /api/order-items
GET /api/user-profiles
# Mauvais : camelCase ou snake_case
GET /api/orderItems
GET /api/user_profiles
Les URLs ne sont pas sensibles a la casse partout, et le kebab-case est la convention du web. C'est aussi plus lisible dans un navigateur.
Les query params pour filtrer
L'URL identifié la ressource. Les query parameters servent a filtrer, trier, paginer.
http# Filtrer les utilisateurs actifs
GET /api/users?status=active
# Filtrer + trier
GET /api/users?status=active&sort=createdAt
# Paginer
GET /api/users?page=2&limit=20
# Rechercher
GET /api/users?search=alice
# Combiner
GET /api/users?status=active&role=admin&sort=-createdAt&page=1&limit=10
En TypeScript avec Express :
typescriptrouter.get("/api/users", async (req, res) => {
const { status, role, sort, page = "1", limit = "20" } = req.query;
const filters = {
...(status && { status: String(status) }),
...(role && { role: String(role) }),
};
const users = await userService.findAll({
filters,
sort: String(sort || "createdAt"),
page: Number(page),
limit: Math.min(Number(limit), 100), // cap a 100
});
res.json(users);
});
On detaillera la pagination dans l'article 06 et le filtrage dans l'article 07.
Quelques conventions que j'applique systématiquement
- Prefix
/apiou/api/v1: séparé l'API du frontend - Pas de trailing slash :
/api/userset pas/api/users/ - Pas d'extension :
/api/userset pas/api/users.json - IDs dans le path :
/api/users/42et pas/api/users?id=42 - Pas de verbes sauf pour les actions :
/api/users/42/activateOK,/api/getUser/42non
Tu trouveras des exemples concrets et des conventions d'équipe sur paltemps.fr.
Résumé
- Les URLs REST identifient des ressources avec des noms au pluriel, pas des verbes
- La hiérarchie reflete les relations :
/users/42/orders - Kebab-case pour les URLs, pas de trailing slash, pas d'extension
- Les query params servent au filtrage, tri et pagination
- Ne dépassé pas 3 niveaux de profondeur dans la hiérarchie
Precedent : Les principes REST | Suivant : Les méthodes HTTP
Sources
- Zalando RESTful API Guidelines. https://opensource.zalando.com/restful-api-guidelines/
- Microsoft REST API Guidelines. https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md
- Best Practices for Designing a Pragmatic RESTful API, Vinay Sahni. https://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api