Aller au contenu

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 :

Authorization: Bearer <jwt-clerk>
  • À défaut d'en-tête Authorization, l'API accepte aussi le cookie de session Clerk __session.
  • Le contrôle est fait par ClerkLayer avant le routage : une requête sans jeton valide reçoit 401, même sur un chemin inexistant.
  • Le sub du JWT (identifiant utilisateur Clerk) sert de user_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).

curl -H "Authorization: Bearer $TOKEN" https://api.todo.dunkan.xyz/todo/todo
Réponse 200
[
  {
    "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

Corps
{ "title": "Acheter du pain", "description": "Boulangerie du coin" }
  • 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), fixe status = 'todo', created_at = NOW() et user_id = sub.
  • La réponse ne contient pas la tâche créée : il faut relancer GET /todo/todo pour 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}

Corps
{ "title": "Nouveau titre", "description": "Nouvelle description" }

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.