L'API des utilisateurs et des tâches
Users + tasks, contraintes SQL et traduction en codes HTTP.
Objectifs
À la fin de cette leçon, vous saurez :
- ajouter la table users et sa relation vers tasks ;
- exposer les endpoints CRUD pour les deux ressources ;
- gérer les erreurs SQL proprement (contraintes → codes HTTP).
🔗 Pour vous rafraîchir la mémoire : Pool pg et requêtes paramétrées ($1, $2) · CRUD en SQL · jointures SQL · anatomie d'une requête HTTP
Le schéma complet
CREATE TABLE IF NOT EXISTS users (
id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
nom text NOT NULL UNIQUE,
email text NOT NULL UNIQUE
);
CREATE TABLE IF NOT EXISTS tasks (
id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
titre text NOT NULL CHECK (length(titre) BETWEEN 1 AND 200),
fait boolean NOT NULL DEFAULT false,
user_id integer REFERENCES users(id) ON DELETE CASCADE
);
Deux contraintes nouvelles : CHECK (règle métier en base — le titre ≤ 200 caractères devient inviolable) et ON DELETE CASCADE (supprimer l'utilisateur supprime ses tâches — décision assumée, documentée dans le schéma).
Les endpoints
GET /users liste des utilisateurs
POST /users crée un utilisateur
GET /users/:id/tasks les tâches d'un utilisateur
POST /tasks crée une tâche {titre, user_id}
... + tout le CRUD tasks déjà écrit
La création avec FK
async function creerTache(corps) {
if (!corps.user_id) {
throw new ErreurValidation("user_id requis");
}
const r = await pool.query(
`INSERT INTO tasks (titre, user_id)
VALUES ($1, $2)
RETURNING *`,
[corps.titre, corps.user_id]
);
return r.rows[0];
}
Si user_id: 99 n'existe pas, PostgreSQL rejette avec une erreur de clé étrangère (code SQL 23503). Le serveur la traduit :
try {
const tache = await creerTache(corps);
repondreJSON(reponse, 201, tache);
} catch (erreur) {
if (erreur.code === "23503") {
return repondreJSON(reponse, 400, { erreur: "Utilisateur inconnu" });
}
if (erreur.code === "23505") {
return repondreJSON(reponse, 409, { erreur: "Doublon" }); // violation UNIQUE
}
console.error(erreur);
return repondreJSON(reponse, 500, { erreur: "Erreur interne" });
}
Table de traduction à connaître :
| Code SQL | Signification | Réponse HTTP |
|---|---|---|
| 23503 | clé étrangère violée | 400 |
| 23505 | unicité violée | 409 |
| 23514 | CHECK violée | 400 |
Les erreurs de contrainte deviennent des réponses propres ; le 500 reste réservé aux vraies surprises.
Exercice
- Ajoutez users + les modifications du schéma.
- Implémentez POST /users avec gestion du doublon email → 409.
- Implémentez GET /users/:id/tasks (LEFT JOIN pour inclure le compteur ?).
- Testez la cascade : supprimez un utilisateur puis vérifiez ses tâches.
Résumé
- Contraintes CHECK/FK : la base protège ce que la validation pourrait manquer.
- Codes d'erreur SQL → statuts HTTP : 23503→400, 23505→409.
- CASCADE est une décision de produit, pas un détail technique.
Correction disponibleCherchez d’abord par vous-même.Voir la correction
Correction
Réponses détaillées
Question 3.
SELECT t.*, COUNT(t.id) OVER () ... -- ou simplement :
SELECT t.* FROM tasks t WHERE t.user_id = $1 ORDER BY t.id;
Version avec total :
SELECT u.nom,
COUNT(t.id) FILTER (WHERE NOT t.fait) AS a_faire,
COUNT(t.id) FILTER (WHERE t.fait) AS terminees
FROM users u LEFT JOIN tasks t ON t.user_id = u.id
WHERE u.id = $1 GROUP BY u.nom;