Référence API¶
| URL de base (prod) | https://api.todo.dunkan.xyz |
| URL de base (local) | http://localhost:8080 |
| Format | Requêtes en JSON, réponses en JSON (GET) ou texte brut (autres routes) |
| Authentification | JWT de session Clerk, obligatoire sur toutes les routes |
Préfixe /todo/todo
Le routeur principal monte les routes sous /todo (nest("/todo", …)), et chaque route commence elle-même par /todo. Le chemin complet est donc /todo/todo…, et c'est bien celui qu'utilise le front.
Authentification¶
Chaque requête doit porter un JWT de session Clerk valide :
- À défaut d'en-tête
Authorization, l'API accepte aussi le cookie de session Clerk__session. - Le contrôle est fait par
ClerkLayeravant le routage : une requête sans jeton valide reçoit401, même sur un chemin inexistant. - Le
subdu JWT (identifiant utilisateur Clerk) sert deuser_id: un utilisateur ne peut lire ni modifier que ses propres tâches.
Côté front, le jeton s'obtient avec getToken() de @clerk/clerk-react :
const token = await getToken();
await api.get("/todo/todo", { headers: { Authorization: `Bearer ${token}` } });
Récapitulatif des routes¶
| Méthode | Chemin | Corps | Réponse 200 |
Rôle |
|---|---|---|---|---|
GET |
/todo/todo |
– | JSON : tableau de ToDo |
Lister ses tâches |
POST |
/todo/todo |
{ title, description } |
texte done |
Créer une tâche |
PUT |
/todo/todo/{id} |
{ title, description } |
texte done |
Modifier titre et description |
DELETE |
/todo/todo/{id} |
– | texte done |
Supprimer une tâche |
POST |
/todo/todo/done/{id} |
– | texte done |
Passer le statut à done |
POST |
/todo/todo/undone/{id} |
– | texte done |
Repasser le statut à todo |
{id} est l'identifiant entier (id) de la tâche, pas son uuid.
Détail des routes¶
Lister ses tâches¶
GET /todo/todo
Renvoie toutes les tâches dont user_id vaut le sub du JWT, sans ordre garanti (pas d'ORDER BY).
[
{
"id": 1,
"uuid": "6f1c2a4e-3b7d-4c55-9a51-2f0d8e1b7c90",
"title": "Acheter du pain",
"description": "Boulangerie du coin",
"status": "todo",
"created_at": "2025-12-26T22:15:03.512345Z"
}
]
| Code | Cas |
|---|---|
200 |
Liste (éventuellement vide []) |
401 |
Jeton absent ou invalide |
500 |
Erreur SQL, ou ligne avec description à NULL (corps : Unable to fetch users) |
Créer une tâche¶
POST /todo/todo
- Les deux champs sont obligatoires et de type chaîne (une chaîne vide est acceptée).
- L'API génère le
uuid(v4), fixestatus = 'todo',created_at = NOW()etuser_id = sub. - La réponse ne contient pas la tâche créée : il faut relancer
GET /todo/todopour l'obtenir (c'est ce que fait le front).
curl -X POST https://api.todo.dunkan.xyz/todo/todo \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"title":"Acheter du pain","description":"Boulangerie du coin"}'
| Code | Cas |
|---|---|
200 |
Corps texte done (pas de 201) |
400 |
JSON mal formé |
401 |
Jeton absent ou invalide |
415 |
En-tête Content-Type: application/json absent |
422 |
Champ manquant ou de mauvais type |
500 |
Erreur SQL (Unable to fetch users) |
Modifier une tâche¶
PUT /todo/todo/{id}
Remplace les deux champs (pas de mise à jour partielle). Le statut n'est pas modifiable par cette route.
| Code | Cas |
|---|---|
200 |
done, même si aucune tâche ne correspond (id inexistant ou d'un autre utilisateur) |
400 |
id non entier ou JSON mal formé |
401 |
Jeton absent ou invalide |
415 / 422 |
Comme pour la création |
500 |
Erreur SQL |
Supprimer une tâche¶
DELETE /todo/todo/{id}
| Code | Cas |
|---|---|
200 |
done, même si rien n'a été supprimé |
400 |
id non entier |
401 |
Jeton absent ou invalide |
500 |
Erreur SQL |
Changer le statut¶
POST /todo/todo/done/{id} passe status à done ; POST /todo/todo/undone/{id} le repasse à todo. Pas de corps attendu.
| Code | Cas |
|---|---|
200 |
done, même si aucune tâche ne correspond |
400 |
id non entier |
401 |
Jeton absent ou invalide |
500 |
Erreur SQL |
Codes d'erreur¶
| Code | Origine | Corps |
|---|---|---|
400 |
Axum : id non entier, JSON syntaxiquement invalide |
texte |
401 |
ClerkLayer : jeton absent, invalide ou expiré |
texte, ex. Error: No Authorization header or session cookie found on the request payload! |
404 |
Chemin inconnu (avec jeton valide) | vide |
405 |
Méthode non prévue sur un chemin existant (avec jeton valide) | vide |
415 |
Axum : Content-Type JSON manquant |
texte |
422 |
Axum : champ manquant ou mal typé | texte |
500 |
Erreur SQL (handlers) | Unable to fetch users (même message pour toutes les routes) |
Pas de 404 métier
Les routes PUT, DELETE, done et undone ne vérifient pas le nombre de lignes modifiées : un id inexistant ou appartenant à un autre utilisateur donne 200 done sans rien changer.
Modèle de données¶
ToDo (réponse de GET)¶
| Champ | Type JSON | Type Rust | Description |
|---|---|---|---|
id |
entier | i32 |
Clé primaire, utilisée dans les URL |
uuid |
chaîne (UUID) | Uuid |
Identifiant UUID v4 généré par l'API, non utilisé par les routes |
title |
chaîne | String |
Titre |
description |
chaîne | String |
Description (non nulle côté Rust) |
status |
chaîne | String |
todo ou done |
created_at |
chaîne (RFC 3339, UTC) | DateTime<Utc> |
Date de création |
user_id n'est jamais renvoyé.
ToDoCreateBody / ToDoUpdateBody¶
| Champ | Type | Obligatoire |
|---|---|---|
title |
chaîne | oui |
description |
chaîne | oui |
Schéma SQL¶
Migration unique migrations/20251107230825_init.sql :
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE TABLE todos (
id SERIAL PRIMARY KEY,
uuid UUID NOT NULL DEFAULT gen_random_uuid(),
title VARCHAR(255) NOT NULL,
description TEXT,
status VARCHAR(50) NOT NULL DEFAULT 'todo',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
user_id TEXT NOT NULL
);
| Colonne | Type | Contraintes | Remarque |
|---|---|---|---|
id |
SERIAL |
clé primaire | |
uuid |
UUID |
non nul, défaut gen_random_uuid() |
L'API fournit sa propre valeur (v4) |
title |
VARCHAR(255) |
non nul | Un titre de plus de 255 caractères provoque un 500 |
description |
TEXT |
nullable | Nullable en base mais attendu non nul par l'API |
status |
VARCHAR(50) |
non nul, défaut 'todo' |
Aucune contrainte CHECK : seules les valeurs todo et done sont écrites par l'API |
created_at |
TIMESTAMPTZ |
non nul, défaut NOW() |
|
user_id |
TEXT |
non nul | sub Clerk du propriétaire ; pas d'index |
Application de la migration : voir Développement.