Documentation de tous les points d'entrée (endpoints) de l'API Bibliothèque : méthode, URL,
authentification et rôle requis, paramètres, corps attendu et retourné, codes HTTP et erreurs
possibles, avec un exemple curl complet pour chacun.
- URL de base :
http://localhost:8080 - Préfixe des ressources :
/api/v1 - Format : JSON, UTF-8
- Conventions générales
- Authentification
- Profil personnel (« moi »)
- Utilisateurs
- Auteurs
- Catégories
- Livres
- Emprunts
- Observabilité
Toutes les réponses partagent la même structure. Le client teste d'abord succes, puis lit
donnees (ou erreur).
Succès :
{
"succes": true,
"donnees": { "...": "..." },
"meta": { "page": 1, "taille_par_page": 20, "total_elements": 28, "total_pages": 2 }
}meta n'est présent que pour les listes paginées. Un DELETE réussi renvoie 204 No Content (aucun corps).
Erreur :
{
"succes": false,
"erreur": {
"code": "VALIDATION",
"message": "Un ou plusieurs champs sont invalides.",
"details": { "email": "adresse e-mail invalide" }
}
}details (objet champ → explication) n'apparaît que pour les erreurs de validation (422).
Sécurité. Aucune erreur ne divulgue de détail technique (SQL, chemin…). Les erreurs
5xx
sont journalisées côté serveur avec leur cause complète et unidentifiant_requete, mais le
client ne reçoit qu'un message générique.
Les routes protégées attendent le jeton d'accès JWT dans l'en-tête :
Authorization: Bearer <jeton_acces>
- Le jeton d'accès est à courte durée (15 min par défaut). S'il expire, on obtient un
nouveau couple via
POST /api/v1/auth/rafraichir(sans se ré-identifier). - Le refresh token est à longue durée (7 jours) et subit une rotation : à chaque rafraîchissement, l'ancien est révoqué et remplacé.
Récupérer et mémoriser un jeton (exemples ci-dessous) :
JETON=$(curl -s -X POST http://localhost:8080/api/v1/auth/connexion \
-H "Content-Type: application/json" \
-d '{"email":"admin@bibliotheque.fr","mot_de_passe":"MotDePasse123!"}' \
| jq -r '.donnees.jetons.jeton_acces')Trois rôles : admin, bibliothecaire, membre.
| Domaine | Public | membre |
bibliothecaire |
admin |
|---|---|---|---|---|
| Consulter le catalogue (livres/auteurs/catégories) | ✅ | ✅ | ✅ | ✅ |
Profil personnel (/moi) |
✅ | ✅ | ✅ | |
| Emprunter / rendre (pour soi) | ✅ | ✅ | ✅ | |
| Emprunter pour un autre membre | ✅ | ✅ | ||
| Écrire dans le catalogue (créer/modifier/supprimer) | ✅ | ✅ | ||
| Lister/consulter tous les emprunts | ✅ | ✅ | ||
| Gérer les utilisateurs | ✅ | |||
Suppression définitive (?definitif=true) |
✅ |
Les endpoints de liste acceptent :
| Paramètre | Défaut | Description |
|---|---|---|
page |
1 |
Numéro de page (≥ 1) |
taille |
20 |
Éléments par page (max 100 ; au-delà, ramené à 100) |
tri |
(par ressource) | Champ de tri, validé par liste blanche (anti-injection) |
ordre |
asc |
asc ou desc |
recherche |
— | Terme de recherche simple (LIKE) |
Les champs de tri autorisés et les filtres dépendent de la ressource (précisés dans chaque
section). Un tri inconnu est ignoré (repli sur le tri par défaut), jamais une erreur.
code métier |
Statut HTTP | Signification |
|---|---|---|
REQUETE_INVALIDE |
400 |
JSON illisible, champ de type incorrect, identifiant mal formé |
NON_AUTHENTIFIE |
401 |
Jeton absent, invalide ou expiré ; identifiants erronés |
INTERDIT |
403 |
Authentifié mais rôle insuffisant ; compte désactivé |
NON_TROUVE |
404 |
Ressource inexistante |
CONFLIT |
409 |
Doublon (UNIQUE), règle métier bloquante (stock, quota, FK) |
REQUETE_INVALIDE |
413 |
Corps de requête trop volumineux |
VALIDATION |
422 |
Requête bien formée mais champs invalides (details fourni) |
TROP_DE_REQUETES |
429 |
Limite de débit atteinte (en-tête Retry-After) |
ERREUR_INTERNE |
500 |
Erreur serveur inattendue (cause journalisée, non exposée) |
SERVICE_INDISPONIBLE |
503 |
Dépendance en panne (ex. base injoignable, via /ready) |
Routes publiques sous /api/v1/auth.
Crée un compte membre (le rôle est forcé à membre — protection anti Mass-Assignment).
-
Auth : aucune.
-
Corps :
Champ Type Règles emailstring requis, e-mail valide, ≤ 254 caractères mot_de_passestring requis, 8 à 72 caractères nomstring requis, ≤ 100 caractères prenomstring requis, ≤ 100 caractères -
Réponse :
201 Created— l'objetUtilisateur. -
Erreurs :
400(JSON invalide / champ non autorisé),422(validation),409(e-mail déjà utilisé).
curl -s -X POST http://localhost:8080/api/v1/auth/inscription \
-H "Content-Type: application/json" \
-d '{
"email": "nouveau.membre@exemple.fr",
"mot_de_passe": "MotDePasse123!",
"nom": "Nouveau",
"prenom": "Membre"
}' | jq{
"succes": true,
"donnees": {
"id": "5f1c…-uuid",
"email": "nouveau.membre@exemple.fr",
"nom": "Nouveau",
"prenom": "Membre",
"role": "membre",
"actif": true,
"cree_le": "2026-07-06T10:00:00Z",
"modifie_le": "2026-07-06T10:00:00Z"
}
}Vérifie les identifiants et renvoie le profil et une paire de jetons.
- Auth : aucune.
- Corps :
{ "email": string, "mot_de_passe": string }(tous deux requis). - Réponse :
200 OK—{ "utilisateur": Utilisateur, "jetons": PaireDeJetons }. - Erreurs :
400,422,401(identifiants invalides — message identique que l'e-mail soit inconnu ou le mot de passe erroné, pour ne pas révéler l'existence d'un compte),403(compte désactivé).
curl -s -X POST http://localhost:8080/api/v1/auth/connexion \
-H "Content-Type: application/json" \
-d '{"email":"admin@bibliotheque.fr","mot_de_passe":"MotDePasse123!"}' | jq{
"succes": true,
"donnees": {
"utilisateur": { "id": "…", "email": "admin@bibliotheque.fr", "role": "admin", "actif": true, "…": "…" },
"jetons": {
"jeton_acces": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…",
"jeton_rafraichissement": "R2c3…opaque",
"type_jeton": "Bearer",
"expire_le": "2026-07-06T10:15:00Z"
}
}
}Échange un refresh token valide contre une nouvelle paire de jetons (rotation : l'ancien est révoqué).
- Auth : aucune (le refresh token fait foi).
- Corps :
{ "jeton_rafraichissement": string }. - Réponse :
200 OK—PaireDeJetons. - Erreurs :
400(jeton manquant),401(invalide / expiré / révoqué),403(compte désactivé).
curl -s -X POST http://localhost:8080/api/v1/auth/rafraichir \
-H "Content-Type: application/json" \
-d '{"jeton_rafraichissement":"R2c3…opaque"}' | jqRévoque le refresh token fourni. Le jeton d'accès reste techniquement valide jusqu'à sa courte
expiration (limite connue des JWT sans liste de révocation, acceptable vu la durée réduite).
- Auth : aucune.
- Corps :
{ "jeton_rafraichissement": string }. - Réponse :
204 No Content. - Erreurs :
400(jeton manquant).
curl -s -o /dev/null -w "%{http_code}\n" -X POST http://localhost:8080/api/v1/auth/deconnexion \
-H "Content-Type: application/json" \
-d '{"jeton_rafraichissement":"R2c3…opaque"}'
# 204Routes authentifiées, tout rôle. L'identité provient du jeton (jamais du corps ou de l'URL).
Renvoie le profil de l'utilisateur connecté.
- Auth : Bearer (tout rôle). Réponse :
200 OK—Utilisateur. Erreurs :401.
curl -s -H "Authorization: Bearer $JETON" http://localhost:8080/api/v1/moi | jqModifie son propre nom / prenom. Les champs role et actif sont ignorés (un membre
ne peut pas s'auto-promouvoir).
- Auth : Bearer (tout rôle).
- Corps (partiel) :
{ "nom"?: string, "prenom"?: string }. - Réponse :
200 OK—Utilisateur. Erreurs :400,401,422.
curl -s -X PATCH http://localhost:8080/api/v1/moi \
-H "Authorization: Bearer $JETON" -H "Content-Type: application/json" \
-d '{"prenom":"Chloé-Anne"}' | jqListe mes emprunts (paginée).
- Auth : Bearer (tout rôle).
- Filtre :
statut(en_cours|rendu|en_retard). Tri :date_emprunt(défaut),date_retour,statut. - Réponse :
200 OK— liste d'Emprunt+meta.
curl -s -H "Authorization: Bearer $JETON" \
"http://localhost:8080/api/v1/moi/emprunts?statut=en_cours&tri=date_retour&ordre=asc" | jqIndicateurs d'emprunt de l'utilisateur connecté (via la procédure pr_statistiques_utilisateur).
- Auth : Bearer (tout rôle). Réponse :
200 OK.
curl -s -H "Authorization: Bearer $JETON" http://localhost:8080/api/v1/moi/statistiques | jq{
"succes": true,
"donnees": { "nb_total": 5, "nb_en_cours": 2, "nb_en_retard": 1, "total_penalites": 8.0 }
}Routes réservées à l'administrateur (admin), sous /api/v1/utilisateurs.
- Tri autorisé :
nom,email,role,date(→cree_le, défaut). - Filtre :
role. Recherche : surnom,prenom,email.
| Méthode & URL | Corps | Succès | Erreurs notables |
|---|---|---|---|
GET /api/v1/utilisateurs |
— | 200 |
401, 403 |
POST /api/v1/utilisateurs |
CreerUtilisateurEntree |
201 |
400, 401, 403, 422, 409 |
GET /api/v1/utilisateurs/{id} |
— | 200 |
400, 401, 403, 404 |
PUT /api/v1/utilisateurs/{id} |
{ "nom", "prenom" } |
200 |
400, 401, 403, 404, 422 |
PATCH /api/v1/utilisateurs/{id} |
{ "nom"?, "prenom"?, "role"?, "actif"? } |
200 |
400, 401, 403, 404, 422 |
DELETE /api/v1/utilisateurs/{id} |
— (?definitif=true pour physique) |
204 |
401, 403, 404 |
CreerUtilisateurEntree : { "email", "mot_de_passe", "nom", "prenom", "role" } où role ∈
{admin, bibliothecaire, membre}. Contrairement à l'inscription publique, l'admin fixe le rôle.
En PATCH, les champs role et actif ne sont appliqués que par un admin (garanti par la route).
DELETE fait une suppression logique (compte désactivé et masqué) ; avec ?definitif=true,
suppression physique (les emprunts liés partent en cascade).
# Créer un bibliothécaire (admin requis)
curl -s -X POST http://localhost:8080/api/v1/utilisateurs \
-H "Authorization: Bearer $JETON_ADMIN" -H "Content-Type: application/json" \
-d '{
"email": "biblio2@bibliotheque.fr",
"mot_de_passe": "MotDePasse123!",
"nom": "Nour", "prenom": "Sami",
"role": "bibliothecaire"
}' | jq
# Lister les membres, triés par nom
curl -s -H "Authorization: Bearer $JETON_ADMIN" \
"http://localhost:8080/api/v1/utilisateurs?role=membre&tri=nom&taille=50" | jq
# Désactiver un compte (PATCH, admin)
curl -s -X PATCH http://localhost:8080/api/v1/utilisateurs/<UUID> \
-H "Authorization: Bearer $JETON_ADMIN" -H "Content-Type: application/json" \
-d '{"actif": false}' | jq
# Suppression définitive
curl -s -o /dev/null -w "%{http_code}\n" -X DELETE \
"http://localhost:8080/api/v1/utilisateurs/<UUID>?definitif=true" \
-H "Authorization: Bearer $JETON_ADMIN"Lecture publique ; écriture réservée à bibliothecaire/admin. Sous /api/v1/auteurs.
- Tri autorisé :
nom(défaut),date_naissance,date(→cree_le). - Filtre :
nationalite. Recherche : surnom,prenom.
| Méthode & URL | Auth | Corps | Succès | Erreurs notables |
|---|---|---|---|---|
GET /api/v1/auteurs |
Public | — | 200 |
— |
GET /api/v1/auteurs/{id} |
Public | — | 200 |
400, 404 |
POST /api/v1/auteurs |
biblio/admin | CreerAuteurEntree |
201 |
400, 401, 403, 422 |
PUT /api/v1/auteurs/{id} |
biblio/admin | MettreAJourAuteurEntree |
200 |
400, 401, 403, 404, 422 |
PATCH /api/v1/auteurs/{id} |
biblio/admin | ModifierAuteurEntree |
200 |
400, 401, 403, 404, 422 |
DELETE /api/v1/auteurs/{id} |
biblio/admin | — | 204 |
401, 403, 404, 409 |
Champs d'Auteur (entrée) : nom (requis à la création), prenom, nationalite,
date_naissance (facultatif, format AAAA-MM-JJ, validé), biographie. La suppression est
physique ; elle renvoie 409 si des livres référencent l'auteur (FK ON DELETE RESTRICT).
# Créer un auteur (biblio/admin)
curl -s -X POST http://localhost:8080/api/v1/auteurs \
-H "Authorization: Bearer $JETON" -H "Content-Type: application/json" \
-d '{
"nom": "Zola", "prenom": "Émile",
"nationalite": "française", "date_naissance": "1840-04-02",
"biographie": "Chef de file du naturalisme."
}' | jq
# Rechercher des auteurs français nommés « hugo »
curl -s "http://localhost:8080/api/v1/auteurs?recherche=hugo&nationalite=française" | jqLecture publique ; écriture réservée à bibliothecaire/admin. Sous /api/v1/categories.
- Tri autorisé :
nom(défaut),date(→cree_le). Recherche : surnom. Aucun filtre.
| Méthode & URL | Auth | Corps | Succès | Erreurs notables |
|---|---|---|---|---|
GET /api/v1/categories |
Public | — | 200 |
— |
GET /api/v1/categories/{id} |
Public | — | 200 |
400, 404 |
POST /api/v1/categories |
biblio/admin | { "nom", "description" } |
201 |
400, 401, 403, 422, 409 |
PUT /api/v1/categories/{id} |
biblio/admin | { "nom", "description" } |
200 |
400, 401, 403, 404, 409 |
PATCH /api/v1/categories/{id} |
biblio/admin | { "nom"?, "description"? } |
200 |
400, 401, 403, 404, 409 |
DELETE /api/v1/categories/{id} |
biblio/admin | — | 204 |
401, 403, 404, 409 |
Le nom est unique : un doublon renvoie 409. La suppression renvoie 409 si des
livres appartiennent à la catégorie.
curl -s -X POST http://localhost:8080/api/v1/categories \
-H "Authorization: Bearer $JETON" -H "Content-Type: application/json" \
-d '{"nom":"Théâtre","description":"Pièces et textes dramatiques"}' | jqLecture publique ; écriture réservée à bibliothecaire/admin. Sous /api/v1/livres.
- Tri autorisé :
titre(défaut),annee(→annee_publication),prix,date(→cree_le). - Filtres :
categorie(UUID de catégorie),auteur(UUID d'auteur),disponible=true. Recherche : surtitre.
| Méthode & URL | Auth | Corps | Succès | Erreurs notables |
|---|---|---|---|---|
GET /api/v1/livres |
Public | — | 200 |
— |
GET /api/v1/livres/{id} |
Public | — | 200 |
400, 404 |
POST /api/v1/livres |
biblio/admin | CreerLivreEntree |
201 |
400, 401, 403, 422, 409 |
PUT /api/v1/livres/{id} |
biblio/admin | MettreAJourLivreEntree |
200 |
400, 401, 403, 404, 422, 409 |
PATCH /api/v1/livres/{id} |
biblio/admin | ModifierLivreEntree |
200 |
400, 401, 403, 404, 422, 409 |
DELETE /api/v1/livres/{id} |
biblio/admin | — (?definitif=true : admin) |
204 |
401, 403, 404, 409 |
CreerLivreEntree :
| Champ | Type | Règles |
|---|---|---|
titre |
string | requis, ≤ 255 |
isbn |
string | requis, ISBN-13 valide (clé de contrôle vérifiée) |
auteur_id |
string | requis, UUID d'un auteur existant |
categorie_id |
string | requis, UUID d'une catégorie existante |
annee_publication |
int | 1400 à 2200 |
nombre_exemplaires |
int | 1 à 100 000 |
resume |
string | facultatif |
prix |
number | ≥ 0 |
langue |
string | facultatif, ≤ 50 (défaut « français ») |
À la création, exemplaires_disponibles = nombre_exemplaires. Un auteur_id / categorie_id
inexistant renvoie 422 (référence invalide). Un isbn en doublon renvoie 409. En
mise à jour, réduire nombre_exemplaires sous le nombre d'exemplaires actuellement empruntés
renvoie 409. La lecture renvoie l'objet enrichi (nom d'auteur, nom de catégorie,
disponible) issu de la vue vue_livres_details.
DELETE fait une suppression logique (masquée du catalogue, historique préservé) ; avec
?definitif=true (admin uniquement), suppression physique, qui renvoie 409 si des
emprunts référencent le livre.
# Créer un livre (récupérez d'abord des UUID d'auteur et de catégorie)
AUTEUR=$(curl -s "http://localhost:8080/api/v1/auteurs?recherche=Camus" | jq -r '.donnees[0].id')
CATEG=$(curl -s "http://localhost:8080/api/v1/categories?recherche=Roman" | jq -r '.donnees[0].id')
curl -s -X POST http://localhost:8080/api/v1/livres \
-H "Authorization: Bearer $JETON" -H "Content-Type: application/json" \
-d "{
\"titre\": \"La Chute\",
\"isbn\": \"9782070360024\",
\"auteur_id\": \"$AUTEUR\",
\"categorie_id\": \"$CATEG\",
\"annee_publication\": 1956,
\"nombre_exemplaires\": 3,
\"prix\": 7.40,
\"resume\": \"Un monologue à Amsterdam.\"
}" | jq
# Consulter un livre
curl -s http://localhost:8080/api/v1/livres/<UUID> | jq
# Ajuster le stock (PATCH partiel)
curl -s -X PATCH http://localhost:8080/api/v1/livres/<UUID> \
-H "Authorization: Bearer $JETON" -H "Content-Type: application/json" \
-d '{"nombre_exemplaires": 5}' | jqExemple de réponse (GET /api/v1/livres/{id}) :
{
"succes": true,
"donnees": {
"id": "…-uuid",
"titre": "L'Étranger",
"isbn": "9782010000096",
"annee_publication": 1942,
"nombre_exemplaires": 5,
"exemplaires_disponibles": 4,
"prix": 7.9,
"langue": "français",
"resume": "Meursault face à l'absurde.",
"auteur_id": "…-uuid",
"auteur": "Albert Camus",
"categorie_id": "…-uuid",
"categorie": "Roman",
"disponible": true,
"cree_le": "2026-07-06T09:00:00Z",
"modifie_le": "2026-07-06T09:00:00Z"
}
}Sous /api/v1/emprunts. Emprunter et rendre sont ouverts à tout utilisateur authentifié (pour
lui-même) ; la consultation globale est réservée à bibliothecaire/admin.
- Tri autorisé (listes) :
date_emprunt(défaut),date_retour(→date_retour_prevue),statut. Filtre :statut.
-
Auth : Bearer (tout rôle).
-
Corps :
Champ Type Règles livre_idstring requis, UUID du livre duree_joursint facultatif, 1 à 90 (défaut 14) utilisateur_idstring facultatif — réservé à biblio/admin pour emprunter au nom d'un membre Un membre ne peut emprunter que pour lui-même : s'il renseigne un
utilisateur_iddifférent du sien, il reçoit403. Le paramètre est ignoré pour un membre qui met son propre UUID. -
Réponse :
201 Created— l'Empruntcréé (enrichi du nom de l'emprunteur et du titre). -
Erreurs :
400/422(entrée),401,403(membre empruntant pour autrui),404(livre ou utilisateur introuvable/inactif),409(aucun exemplaire disponible ou quota de 5 emprunts simultanés atteint).
LIVRE=$(curl -s "http://localhost:8080/api/v1/livres?disponible=true" | jq -r '.donnees[0].id')
curl -s -X POST http://localhost:8080/api/v1/emprunts \
-H "Authorization: Bearer $JETON" -H "Content-Type: application/json" \
-d "{\"livre_id\":\"$LIVRE\",\"duree_jours\":21}" | jqEnregistre le retour et renvoie l'emprunt clôturé (statut rendu, penalite éventuelle calculée
à 0,50 €/jour de retard).
- Auth : Bearer (tout rôle). Réponse :
200 OK—Emprunt. - Erreurs :
400(UUID invalide),401,404(emprunt introuvable),409(déjà rendu).
curl -s -X POST http://localhost:8080/api/v1/emprunts/<UUID>/retour \
-H "Authorization: Bearer $JETON" | jqListe tous les emprunts, avec filtre statut et pagination.
curl -s -H "Authorization: Bearer $JETON_BIBLIO" \
"http://localhost:8080/api/v1/emprunts?statut=en_retard&tri=date_retour&ordre=asc" | jq- Auth : Bearer (
bibliothecaire/admin). Réponse :200 OK—Emprunt. Erreurs :400,401,403,404.
Exemple d'Emprunt :
{
"succes": true,
"donnees": {
"id": "…-uuid",
"date_emprunt": "2026-06-20T00:00:00Z",
"date_retour_prevue": "2026-07-04T00:00:00Z",
"date_retour_effective": "2026-07-06T00:00:00Z",
"statut": "rendu",
"penalite": 1.0,
"utilisateur_id": "…-uuid",
"utilisateur": "Chloé Durand",
"livre_id": "…-uuid",
"livre": "Les Misérables",
"cree_le": "2026-06-20T08:00:00Z",
"modifie_le": "2026-07-06T08:00:00Z"
}
}Routes publiques, hors préfixe /api/v1.
Répond 200 tant que le processus est vivant ; ne dépend d'aucune ressource externe.
curl -s http://localhost:8080/health | jq
# {"succes":true,"donnees":{"statut":"ok","version":"1.0.0","duree_fonctionnement":"1h2m3s"}}Vérifie que PostgreSQL répond (ping borné à 2 s).
- Succès :
200—{ "statut": "pret", "base_de_donnees": "ok" }. - Échec :
503— codeSERVICE_INDISPONIBLE(base injoignable).
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8080/readyRenvoie les métriques au format texte Prometheus (pas l'enveloppe JSON). Principales séries :
| Métrique | Type | Description |
|---|---|---|
bibliotheque_http_requetes_total |
Counter | Requêtes par methode, route, statut |
bibliotheque_http_duree_requete_secondes |
Histogram | Latence par methode, route |
bibliotheque_http_requetes_en_cours |
Gauge | Requêtes en cours de traitement |
| (+ métriques standard Go et processus) | GC, goroutines, CPU, mémoire… |
curl -s http://localhost:8080/metrics | grep '^bibliotheque_'La
routeutilisée en label est le patron (GET /api/v1/livres/{id}), pas le chemin réel,
afin d'éviter l'explosion de cardinalité (une série par identifiant).