On revient à ton generateur de citations.
Tu as fermé Cursor entre temps? tu l'ouvres, tu ouvres le dossier, tu lances claude et comme la dernière fois : python -m http.server dans le terminal, localhost:8000 dans le navigateur (ou un truc approchant, claude est grand, claude te dit). Une citation s'affiche, le bouton "Nouvelle citation" en sort une autre. Tout va bien.
On va lui ajouter quelque chose aujourd'hui. Une fonctionnalité minuscule, qui semble anodine, et qui va faire apparaître un manque qu'on n'avait pas encore nommé.
Demander un formulaire
Ouvre le projet dans Cursor. Lance Claude Code dans le terminal avec claude. Et colle ceci dans la fenêtre de dialogue :
J'aimerais pouvoir ajouter mes propres citations à la liste affichée par le générateur. Ajoute un petit formulaire en bas de la page avec deux champs (le texte de la citation et l'auteur) et un bouton "Ajouter". Quand on clique sur "Ajouter", la citation doit apparaître dans la rotation comme les autres.
Claude Code se met au travail. Il propose ses modifications, tu valides les diffs comme la dernière fois. Le formulaire apparaît.
Tester
Rafraîchis la page. En bas, sous la citation actuelle, tu vois deux champs et un bouton.
Tape la première chose qui te vient. Pour ma part, j'ai tapé "Le mieux est l'ennemi du bien", et "Voltaire" dans le champ auteur. Clic sur "Ajouter".
Ta citation apparaît. Tu cliques sur "Nouvelle citation" plusieurs fois pour la voir tourner avec les autres. Elle est bien là, mêlée aux dix d'origine. Sisyphe, Voltaire, Beauvoir, Voltaire encore. Ça marche.
C'est la première fois que tu modifies une app pour y ajouter quelque chose qui te ressemble. C'est satisfaisant.
Maintenant, rafraîchis la page.
Clique plusieurs fois sur "Nouvelle citation". Cherche ta citation dans la rotation.
Elle n'est nulle part.
La citation qui disparaît
C'est le moment le plus utile de toute cette série. Pas parce que c'est une erreur, mais parce que c'est exactement ce qui devait se passer.
Quand j'ai demandé à Claude Code d'ajouter ce formulaire au générateur, il a fait ce que je lui avais demandé. Un champ de texte, un bouton, un bout de JavaScript qui prend ce que tu tapes et l'ajoute à la liste affichée à l'écran. Ça fonctionne. C'est propre. Et c'est complètement éphémère.
Ce qui s'est passé quand tu as cliqué sur "Ajouter" : ta citation a été stockée dans la mémoire de l'onglet ouvert dans ton navigateur. Pas dans le projet. Pas dans un fichier. Dans la mémoire vive de la page, celle qui s'efface dès qu'on la ferme ou qu'on la recharge. Rafraîchir la page, c'est comme éteindre la lumière dans une pièce. Tout ce qui n'était pas fixé quelque part disparaît.
La citation que tu avais tapée n'était fixée nulle part.
Ce moment, ce petit vide à l'écran après le rafraîchissement, c'est la meilleure définition possible de ce qu'est une base de données et de pourquoi on en a besoin. Tout le reste de cet article est une explication de ce que tu viens de vivre.
Ce que le code peut garder, et ce qu'il ne peut pas
Ouvre le projet dans ton éditeur. Il y a un fichier qui s'appelle quotes.json. C'est là que vivent les citations depuis l'article 4 : une liste de textes et d'auteurs, écrits à la main, avant que l'app tourne pour la première fois.
Ce fichier fait partie du code. Il a été écrit une fois, au moment où on a construit le projet. Quand l'app se lance, elle lit ce fichier et affiche ce qu'il contient. Mais ce fichier ne se réécrit pas tout seul pendant que l'app tourne. Il est figé.
C'est la distinction centrale, et elle est simple. Il y a ce qui existe dans le projet avant qu'il tourne, et il y a ce qui arrive pendant qu'il tourne.
Le code, le JSON, les images, la mise en page : tout ça, c'est le projet. Écrit une fois, déployé, et ça ne change pas entre deux visites. Quand quelqu'un ouvre ton générateur à Paris et que quelqu'un d'autre l'ouvre à Lyon, ils voient exactement la même chose, parce qu'ils lisent le même fichier.
Mais ce que les gens ajoutent en utilisant l'app, ça, c'est différent. La citation que tu viens de taper n'existait pas dans le projet au moment du déploiement. Elle est née pendant l'usage. Et le code n'a aucun mécanisme pour se réécrire lui-même à chaque fois que quelqu'un remplit un formulaire.
Donc cette citation, si on ne lui trouve pas un endroit où vivre en dehors du code, elle disparaît. C'est ce qui s'est passé.
Une base de données, c'est cet endroit. Séparé du code, toujours allumé, conçu pour recevoir ce qui arrive pendant que l'app tourne.
Une table, c'est exactement ce que tu crois
Le mot "base de données" fait peur. Je ne sais pas pourquoi, parce que la structure de base est quelque chose qu'on connaît toutes depuis des années.
Lignes, colonnes, types
Une base de données est organisée en tables. Une table, c'est une feuille de tableur. Vraiment. Si tu ouvres Airtable ou Excel et que tu crées une feuille avec des colonnes "Texte", "Auteur", "Date d'ajout", tu as déjà la structure d'une table de base de données.
Pour notre générateur de citations, la table s'appellerait citations. Elle aurait quatre colonnes : un identifiant unique pour chaque ligne, le texte de la citation, le nom de l'auteur, et la date à laquelle elle a été ajoutée. Chaque ligne serait une citation. "Il faut imaginer Sisyphe heureux" de Camus, une ligne. "On ne naît pas femme, on le devient" de Beauvoir, une autre.
Jusque-là, c'est un tableur.
Plus strict qu'un tableur, et c'est voulu
Il y a une différence avec un tableur, et elle est importante. Dans une base de données, chaque colonne a un type déclaré à l'avance. La colonne "Texte" accepte du texte. La colonne "Date d'ajout" accepte des dates. La colonne "Identifiant" accepte des nombres entiers. Et ces types ne bougent pas.
Dans un tableur, rien ne t'empêche de taper "demain" dans une colonne date, ou de laisser une cellule vide là où il devrait y avoir un nombre. Le tableur hausse les épaules et accepte. Une base de données, elle, refuse. Elle dit : non, cette colonne attend une date au format AAAA-MM-JJ, ce que tu m'envoies n'est pas une date, je ne l'accepte pas.
Au premier abord, ça semble contraignant. En réalité, c'est ce qui fait que les données restent fiables. Quand ton app demande à la base "donne-moi toutes les citations ajoutées après le 1er janvier", elle peut le faire parce qu'elle sait avec certitude que la colonne "Date d'ajout" contient des dates, pas du texte aléatoire.
La rigueur du type, c'est ce qui rend la base interrogeable.
Quand une table ne suffit plus
Imagine qu'on veuille enrichir le générateur. Pas juste le texte de la citation et le nom de l'auteur, mais une courte biographie, l'époque, le pays d'origine. Des informations sur les auteurs, pas sur les citations.
Le problème de l'auteur répété
Si on reste dans une seule table, voilà ce qui se passe. Victor Hugo a dix citations dans la base. Chaque ligne contient "Victor Hugo", "Romancier français, 1802-1885", "France". Dix fois. La même information, recopiée dix fois.
C'est redondant, mais surtout c'est fragile. Le jour où je veux corriger la bio de Victor Hugo, je dois retrouver les dix lignes et les modifier une par une. Si j'en oublie une, la base contient deux versions différentes de la même information. Ce genre d'incohérence, dans une base qui grossit, c'est un vrai problème.
Deux tables, une flèche
La solution est de couper en deux. Une table citations qui contient le texte et une référence vers l'auteur. Une table auteurs qui contient le nom, la bio, l'époque, le pays. Victor Hugo n'apparaît qu'une seule fois dans la table auteurs. Ses dix citations, dans la table citations, pointent toutes vers cette unique ligne.
Chaque chose rangée une seule fois. C'est tout le principe.
Concrètement, la table citations a une colonne supplémentaire : auteur_id. Pour la citation de Victor Hugo, cette colonne contient le numéro de sa ligne dans la table auteurs. Quand l'app veut afficher "Le texte de la citation + la bio de l'auteur", elle lit la citation, voit l'auteur_id, va chercher la ligne correspondante dans auteurs, et assemble les deux.
C'est le lien entre tables. Pas besoin d'un terme technique pour l'instant. C'est une référence de l'une vers l'autre, comme un numéro de page dans un index.
Quand je corrige la bio de Victor Hugo, je la corrige une fois dans auteurs. Toutes les citations qui pointent vers lui héritent automatiquement de la correction. Rien à retrouver, rien à synchroniser.
Ce que Supabase fait de tout ça
À l'article 5, en cartographiant la stack, j'avais noté Supabase avec la mention "on y reviendra". On y est.
Supabase, c'est l'endroit où ces tables vont vivre. Un service en ligne, séparé du code du projet, qui tourne en permanence. Quand ton app est fermée, Supabase continue de garder les données. Quand dix personnes utilisent l'app en même temps, Supabase gère les dix. Quand tu rafraîchis la page, les citations sont toujours là parce qu'elles ne sont plus dans la mémoire de l'onglet. Elles sont dans Supabase.
Ce qui change concrètement pour le générateur : au lieu que le formulaire "Ajouter une citation" écrive dans la mémoire de la page, il enverra les données à Supabase. Supabase les rangera dans la table citations. La prochaine fois que quelqu'un ouvre l'app, l'app demandera à Supabase la liste des citations, et Supabase répondra avec tout ce qu'il a reçu depuis le début.
La citation ne disparaît plus au rafraîchissement. Elle a un endroit où vivre.
On ne va pas connecter Supabase aujourd'hui. Cet article est un article de nommage. On pose les concepts, on comprend pourquoi on en a besoin, on regarde la structure. La connexion technique, les tables à créer dans Supabase, le code qui relie l'app à la base, c'est l'article suivant.
Ce que Claude pourra faire avec, plus tard
Il y a une raison supplémentaire de bien ranger les données maintenant, et elle est plus loin dans la série.
À l'article 13, on parlera des MCP. C'est un mécanisme qui permet à Claude d'aller chercher des informations dans tes outils directement, sans que tu aies à copier-coller quoi que ce soit. Quand on arrivera là, Claude pourra interroger cette base en langage naturel. "Montre-moi toutes les citations ajoutées ce mois-ci" ou "quelles citations as-tu de Simone de Beauvoir", et obtenir une réponse sans qu'on ait écrit une seule ligne de code pour la requête.
Mais pour que ça fonctionne, les données doivent être proprement structurées. Une table citations avec des colonnes typées, une table auteurs avec une référence claire. Si tout est mélangé dans un fichier texte ou dispersé dans des colonnes sans cohérence, Claude ne peut pas s'y retrouver mieux qu'un humain.
Le soin qu'on prend maintenant à bien nommer les colonnes, à séparer ce qui doit l'être, à déclarer les types : c'est ce qui rendra la base interrogeable plus tard, par du code comme par Claude.