[{"data":1,"prerenderedAt":1369},["ShallowReactive",2],{"blog-fr-duplicate-key-value-violates-unique-constraint":3,"blog-index-fr":841},{"id":4,"title":5,"body":6,"category":800,"cover":801,"coverAlt":802,"description":803,"draft":804,"extension":805,"faq":806,"image":820,"keywords":821,"meta":830,"navigation":831,"ogTitle":5,"path":832,"published":833,"seo":834,"stem":835,"tldr":836,"updated":833,"__hash__":840},"blog_fr\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint.md","Supabase \"duplicate key value violates unique constraint\" expliqué",{"type":7,"value":8,"toc":788},"minimark",[9,13,24,31,34,39,42,45,48,167,180,184,193,267,270,278,292,296,299,302,324,337,343,366,379,385,389,392,399,407,413,419,432,443,447,450,477,514,520,540,556,559,564,568,571,592,607,615,631,634,640,662,667,671,674,683,706,715,733,737,778],[10,11,12],"p",{},"Quelqu'un remplit le formulaire de votre liste d'attente, attend, et appuie de\nnouveau sur le bouton. Le premier clic était déjà passé. Le second revient en\nerreur, et la console du navigateur affiche tout :",[14,15,20],"pre",{"className":16,"code":18,"language":19},[17],"language-text","{\n  \"code\": \"23505\",\n  \"details\": \"Key (email)=(maria@example.com) already exists.\",\n  \"hint\": null,\n  \"message\": \"duplicate key value violates unique constraint \\\"waitlist_email_key\\\"\"\n}\n","text",[21,22,18],"code",{"__ignoreMap":23},"",[10,25,26,27,30],{},"Supabase \"duplicate key value violates unique constraint\" signifie que Postgres a\nrefusé d'ajouter la ligne parce qu'une autre ligne contient déjà la même valeur\ndans une colonne qui n'accepte chaque valeur qu'une fois. Ici la colonne est\n",[21,28,29],{},"email",", et Maria est déjà sur votre liste.",[10,32,33],{},"Le conseil qui circule pour cette erreur est de passer l'insert en upsert. C'est\nparfois juste. Sur une table où vos visiteurs peuvent seulement ajouter, pourtant,\nun upsert depuis le navigateur a besoin de règles qui les laissent lire la table,\net la ligne qui contient l'adresse de Maria ne devrait jamais s'afficher devant un\nvisiteur.",[35,36,38],"h2",{"id":37},"que-signifie-duplicate-key-value-violates-unique-constraint","Que signifie \"duplicate key value violates unique constraint\" ?",[10,40,41],{},"La valeur que vous vouliez enregistrer se trouve déjà dans une colonne qui\nn'accepte chaque valeur qu'une fois, alors Postgres a refusé toute la ligne. Rien\nn'a été écrit, et la ligne arrivée la première n'a pas bougé.",[10,43,44],{},"Imaginez la table comme la liste des invités à une porte, une ligne par nom. Une\ncontrainte unique, c'est la règle du videur : un nom n'entre sur la liste qu'une\nfois. Votre application s'est présentée avec un nom déjà inscrit, et il l'a\nrenvoyée.",[10,46,47],{},"Chaque partie de l'erreur dit une chose :",[49,50,51,67],"table",{},[52,53,54],"thead",{},[55,56,57,61,64],"tr",{},[58,59,60],"th",{},"Champ",[58,62,63],{},"Exemple",[58,65,66],{},"Ce qu'il vous dit",[68,69,70,96,111,126,141],"tbody",{},[55,71,72,77,82],{},[73,74,75],"td",{},[21,76,21],{},[73,78,79],{},[21,80,81],{},"23505",[73,83,84,85,88,89],{},"Le code Postgres de cette erreur, nommé ",[21,86,87],{},"unique_violation"," dans ",[90,91,95],"a",{"href":92,"rel":93},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Futils\u002Ferrcodes.txt",[94],"nofollow","sa liste de codes",[55,97,98,103,108],{},[73,99,100],{},[21,101,102],{},"message",[73,104,105],{},[21,106,107],{},"duplicate key value violates unique constraint \"waitlist_email_key\"",[73,109,110],{},"Quelle règle a refusé la ligne, par son nom",[55,112,113,118,123],{},[73,114,115],{},[21,116,117],{},"details",[73,119,120],{},[21,121,122],{},"Key (email)=(maria@example.com) already exists.",[73,124,125],{},"La colonne et la valeur en conflit, quand Postgres juge que l'appelant peut les voir",[55,127,128,133,138],{},[73,129,130],{},[21,131,132],{},"hint",[73,134,135],{},[21,136,137],{},"null",[73,139,140],{},"Rien à ajouter pour cette erreur",[55,142,143,146,151],{},[73,144,145],{},"Statut HTTP",[73,147,148],{},[21,149,150],{},"409 Conflict",[73,152,153,154,157,158,160,161,166],{},"La façon dont PostgREST, la partie de Supabase qui transforme les appels ",[21,155,156],{},"supabase.from(...)"," en SQL, signale ",[21,159,81],{}," (",[90,162,165],{"href":163,"rel":164},"https:\u002F\u002Fgithub.com\u002FPostgREST\u002Fpostgrest\u002Fblob\u002Fmain\u002Fsrc\u002Flibrary\u002FPostgREST\u002FError.hs",[94],"son code",")",[10,168,169,170,175,176,179],{},"La formulation vient de Postgres lui-même.\n",[90,171,174],{"href":172,"rel":173},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Faccess\u002Fnbtree\u002Fnbtinsert.c",[94],"Le code qui lève l'erreur","\nn'ajoute la ligne ",[21,177,178],{},"Key ... already exists"," que s'il a une description de la clé à\ndonner, et une section plus bas montre quand il n'en a pas.",[35,181,183],{"id":182},"quelle-colonne-a-provoqué-lerreur","Quelle colonne a provoqué l'erreur ?",[10,185,186,187,192],{},"Le nom entre guillemets vous le dit, car Postgres le construit à partir de la\ntable et des colonnes, sauf si quelqu'un en a choisi un autre.\n",[90,188,191],{"href":189,"rel":190},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Fcommands\u002Findexcmds.c",[94],"Son code de nommage","\ndonne trois formes :",[49,194,195,207],{},[52,196,197],{},[55,198,199,202,205],{},[58,200,201],{},"Le nom finit par",[58,203,204],{},"C'est",[58,206,63],{},[68,208,209,233,251],{},[55,210,211,216,221],{},[73,212,213],{},[21,214,215],{},"_key",[73,217,218,219],{},"une contrainte unique sur les colonnes entre le nom de la table et ",[21,220,215],{},[73,222,223,226,227,230,231],{},[21,224,225],{},"waitlist_email_key"," : table ",[21,228,229],{},"waitlist",", colonne ",[21,232,29],{},[55,234,235,240,246],{},[73,236,237],{},[21,238,239],{},"_pkey",[73,241,242,243],{},"la clé primaire de la table, en général ",[21,244,245],{},"id",[73,247,248],{},[21,249,250],{},"orders_pkey",[55,252,253,259,262],{},[73,254,255,258],{},[21,256,257],{},"_idx",", ou autre chose",[73,260,261],{},"un index unique créé à la main, ou une contrainte à qui quelqu'un a donné un nom",[73,263,264],{},[21,265,266],{},"profiles_username_idx",[10,268,269],{},"Le message dit \"unique constraint\" pour les trois. Pour voir exactement ce que\ncouvre un nom, exécutez ceci dans le SQL Editor avec le nom de votre message :",[14,271,276],{"className":272,"code":274,"language":275,"meta":23},[273],"language-sql","select tablename, indexdef\nfrom pg_indexes\nwhere indexname = 'waitlist_email_key';\n","sql",[21,277,274],{"__ignoreMap":23},[10,279,280,283,284,287,288,291],{},[21,281,282],{},"indexdef"," revient sous une forme comme\n",[21,285,286],{},"CREATE UNIQUE INDEX waitlist_email_key ON public.waitlist USING btree (email)",",\net les colonnes entre parenthèses sont celles qui ne peuvent pas se répéter. Une\nrègle sur deux colonnes, comme ",[21,289,290],{},"(team_id, email)",", laisse la même adresse\nrejoindre deux équipes et la refuse une deuxième fois dans la même.",[35,293,295],{"id":294},"key-id1-already-exists-alors-que-vous-navez-jamais-envoyé-did","\"Key (id)=(1) already exists\" alors que vous n'avez jamais envoyé d'id",[10,297,298],{},"Celle-ci signifie que le compteur d'id de la table est en retard sur les lignes\nqu'elle contient déjà, et qu'il a donc distribué un numéro pris. Votre application\nn'a envoyé aucun id. Postgres a pris le numéro suivant au compteur, et une ligne\nportant ce numéro existait déjà.",[10,300,301],{},"Sur la liste des invités, le videur distribue aussi des bracelets numérotés tirés\nd'un rouleau. Quelqu'un a recopié à la main les deux cents premiers invités,\nnuméros de bracelet compris, et personne n'a fait avancer le rouleau. Il repart\ntoujours de 1.",[10,303,304,305,310,311,316,317,319,320,323],{},"Une colonne qui se numérote toute seule prend chaque numéro dans une séquence,\nle nom que Postgres donne à ce compteur. La\n",[90,306,309],{"href":307,"rel":308},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fddl-identity-columns.html",[94],"page de Postgres sur les colonnes d'identité","\ndit qu'une telle colonne \"does not guarantee uniqueness\", et donne ce cas parmi\nles raisons : des valeurs \"could be inserted manually into the identity column\".\nDes lignes arrivent avec leur numéro déjà rempli depuis un import CSV qui contient\nla colonne id, une copie venue d'un autre projet ou un script de seed. Le compteur\nn'entend parler d'aucune.\n",[90,312,315],{"href":313,"rel":314},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Ftroubleshooting\u002Fwhy-are-there-gaps-in-my-postgres-id-sequence-Frifus",[94],"La page de dépannage de Supabase sur les séquences d'id","\ntraite exactement ce message sur un ",[21,318,239],{},", et sa réparation est aussi un\n",[21,321,322],{},"setval",".",[10,325,326,327,330,331,333,334,336],{},"La réparation tient en une instruction. Mettez votre table et votre colonne à la\nplace de ",[21,328,329],{},"orders"," et ",[21,332,245],{}," ; la table est la partie du nom avant ",[21,335,239],{}," :",[14,338,341],{"className":339,"code":340,"language":275,"meta":23},[273],"select setval(\n  pg_get_serial_sequence('public.orders', 'id'),\n  (select max(id) from public.orders)\n);\n",[21,342,340],{"__ignoreMap":23},[10,344,345,348,349,354,355,361,362,323],{},[21,346,347],{},"pg_get_serial_sequence"," trouve le compteur derrière la colonne, et\n",[90,350,353],{"href":351,"rel":352},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Ffunctions-info.html",[94],"la documentation de Postgres","\ndit qu'elle marche pour les deux sortes : \"If the column is an identity column, the\nassociated sequence is the sequence internally created for that column.\" Ensuite\n",[90,356,359],{"href":357,"rel":358},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Ffunctions-sequence.html",[94],[21,360,322],{}," avec\ndeux arguments règle le compteur de sorte que \"the next nextval will advance the\nsequence before returning a value\". La prochaine nouvelle ligne reçoit l'id le\nplus haut plus un. Si Postgres répond que la relation n'existe pas, la table\ns'écrit autrement, porte une majuscule ou se trouve dans un autre schéma, et\n",[90,363,365],{"href":364},"\u002Fblog\u002Fsupabase-relation-does-not-exist","cette erreur a son propre article",[10,367,368,369,372,373,378],{},"Une restauration depuis la sortie de ",[21,370,371],{},"pg_dump"," ne vous amène pas ici.\n",[90,374,377],{"href":375,"rel":376},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fapp-pgdump.html",[94],"Sa documentation","\nénumère ce que contient un dump de données : \"Table data, large objects, and\nsequence values are dumped.\" Chaque compteur revient avec ses lignes.",[380,381],"diagram",{"alt":382,"caption":383,"src":384},"Une liste de cinq lignes numérotées, de 1 à 5, à côté d'un compteur. À gauche, le compteur affiche 1 et désigne la ligne 1, éclairée en ambre, qui aboutit à une plaque Key (id)=(1). À droite, le compteur affiche 6 et désigne un nouveau nom sur la sixième ligne, avec une coche en dessous.","Des lignes recopiées avec leurs propres id laissent le compteur où il était. setval le fait passer au-delà de l'id le plus haut, et la ligne suivante reçoit un numéro libre.","\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint\u002Fthe-counter-is-behind-1600x640.png",[35,386,388],{"id":387},"les-trois-façons-de-traiter-un-doublon","Les trois façons de traiter un doublon",[10,390,391],{},"Décidez ce qui doit se passer quand la valeur est déjà là : refuser la nouvelle\nligne et le dire, la laisser remplacer l'ancienne, ou ne plus l'envoyer du tout.",[10,393,394,398],{},[395,396,397],"strong",{},"Refusez-la, avec vos mots."," Cela convient quand une deuxième ligne serait une\nerreur : une adresse déjà inscrite, un nom d'utilisateur déjà pris. L'insert\néchoue, votre application lit le code, et la personne voit une phrase que vous\navez écrite :",[14,400,405],{"className":401,"code":403,"language":404,"meta":23},[402],"language-js","const { error } = await supabase.from('profiles').insert({ id: user.id, username })\n\nif (error?.code === '23505') {\n  setMessage(\"Ce nom d'utilisateur est pris. Essayez-en un autre.\")\n} else if (error) {\n  setMessage('Un problème est survenu. Veuillez réessayer.')\n}\n","js",[21,406,403],{"__ignoreMap":23},[10,408,409,412],{},[395,410,411],{},"Laissez-la remplacer l'ancienne."," Cela convient quand la version la plus\nrécente doit l'emporter : une ligne de réglages par utilisateur, un panier par\nsession. C'est un upsert, et la section suivante dit quand il tourne mal.",[10,414,415,418],{},[395,416,417],{},"Ne l'envoyez plus."," Parfois la deuxième ligne n'aurait jamais dû exister, et\nelle vient de l'une de ces sources :",[420,421,422,426,429],"ul",{},[423,424,425],"li",{},"un bouton qui renvoie le formulaire quand on appuie une deuxième fois.\nDésactivez-le jusqu'à ce que la première requête réponde.",[423,427,428],{},"une nouvelle tentative après une réponse lente, alors que la première était\npassée",[423,430,431],{},"deux auteurs pour une même ligne, comme un trigger qui crée le profil de chaque\nnouvel utilisateur et du code applicatif qui insère ce même profil après\nl'inscription",[10,433,434,435,439,440,442],{},"Le cas du trigger vaut d'être reconnu au premier coup d'œil. Un trigger qui copie\nles nouveaux utilisateurs dans une table à vous échoue avec cette erreur quand la\nvaleur copiée y est déjà, et Supabase signale toute l'inscription par\n",[90,436,438],{"href":437},"\u002Fblog\u002Fdatabase-error-saving-new-user","\"Database error saving new user\"",". Le\n",[21,441,81],{}," lui-même se trouve dans les logs Postgres, et cet article montre où.",[35,444,446],{"id":445},"faut-il-utiliser-upsert","Faut-il utiliser upsert ?",[10,448,449],{},"Utilisez-le quand la nouvelle ligne doit remplacer l'ancienne, et vérifiez trois\nchoses avant de compter dessus.",[10,451,452,455,456,461,462,465,466,469,470,473,474,323],{},[21,453,454],{},".upsert()"," dans supabase-js est un insert qui fait autre chose quand la ligne\nexiste. ",[90,457,460],{"href":458,"rel":459},"https:\u002F\u002Fgithub.com\u002Fsupabase\u002Fsupabase-js\u002Fblob\u002Fmaster\u002Fpackages\u002Fcore\u002Fpostgrest-js\u002Fsrc\u002FPostgrestQueryBuilder.ts",[94],"Son code source","\nle dit en deux lignes : \"If ",[21,463,464],{},"true",", duplicate rows are ignored. If ",[21,467,468],{},"false",",\nduplicate rows are merged with existing rows.\" C'est l'option ",[21,471,472],{},"ignoreDuplicates",",\net Postgres exécute l'une et l'autre version comme un ",[21,475,476],{},"insert ... on conflict",[10,478,479,486,491,492,495,496,498,499,501,502,504,505,507,508,513],{},[395,480,481,482,485],{},"1. ",[21,483,484],{},"onConflict"," doit nommer la colonne de votre erreur.",[90,487,490],{"href":488,"rel":489},"https:\u002F\u002Fdocs.postgrest.org\u002Fen\u002Fstable\u002Freferences\u002Fapi\u002Ftables_views.html",[94],"La documentation de PostgREST","\ndit : \"By default, upsert operates based on the primary key columns\". Un upsert de\n",[21,493,494],{},"{ email }"," dans une table dont la clé primaire est ",[21,497,245],{}," cherche un conflit sur\n",[21,500,245],{},", n'en trouve pas puisque l'id est neuf, insère, et se heurte à la règle sur\n",[21,503,29],{}," avec le même ",[21,506,81],{},". La\n",[90,509,512],{"href":510,"rel":511},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fsql-insert.html",[94],"référence d'INSERT de Postgres","\ndit que l'action de rechange s'applique \"if an arbiter constraint or index\nspecified by conflict_target is violated\". Un conflit sur toute autre colonne\nrefuse la ligne comme avant.",[14,515,518],{"className":516,"code":517,"language":404,"meta":23},[402],"await supabase\n  .from('settings')\n  .upsert({ user_id: user.id, theme: 'dark' }, { onConflict: 'user_id' })\n",[21,519,517],{"__ignoreMap":23},[10,521,522,525,526,529,530,533,534,539],{},[395,523,524],{},"2. Cette colonne a besoin de sa propre règle unique."," Sans elle, Postgres n'a\nrien à faire correspondre et répond\n",[21,527,528],{},"there is no unique or exclusion constraint matching the ON CONFLICT specification",",\ncode ",[21,531,532],{},"42P10","\n(",[90,535,538],{"href":536,"rel":537},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Foptimizer\u002Futil\u002Fplancat.c",[94],"la ligne qui la lève",").",[10,541,542,545,546,551,552,323],{},[395,543,544],{},"3. Depuis le navigateur, la Row Level Security lit davantage de vos règles."," La\nRow Level Security est l'ensemble des règles par ligne que Supabase vérifie à\nchaque requête faite avec votre clé publique. Pour un upsert, la\n",[90,547,550],{"href":548,"rel":549},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fsql-createpolicy.html",[94],"référence de Postgres sur les policies","\ndit : \"SELECT permissions are required on the relation, and the rows proposed for\ninsertion are checked using the relation's SELECT policies.\" Quand la ligne existe\net que l'upsert fusionne, la ligne existante est aussi vérifiée contre les règles\nde mise à jour de la table. Une ligne qui échoue à l'une de ces vérifications met\nfin à la requête avec\n",[90,553,555],{"href":554},"\u002Fblog\u002Fnew-row-violates-row-level-security-policy","new row violates row-level security policy",[10,557,558],{},"Sur la liste d'attente, un upsert depuis le navigateur échoue donc tant que la\ntable n'a pas de règle de lecture qu'un visiteur respecte. Une règle de lecture\nqu'un inconnu respecte pour n'importe quelle adresse est une règle qui laisse des\ninconnus lire votre liste. À la porte, réécrire une ligne veut dire qu'on vous a\nd'abord montré la liste. Pour une table comme celle-là, refusez plutôt le doublon,\nou servez-vous de la fonction de la section suivante.",[380,560],{"alt":561,"caption":562,"src":563},"Deux requêtes vers la même table. Un insert franchit une barrière, insert, et l'atteint. Un upsert doit franchir trois barrières à la suite, insert, select et update ; la barrière select est fermée et dessinée en ambre, alors l'upsert s'arrête là.","Depuis le navigateur, un upsert est vérifié contre les règles de lecture et de mise à jour de la table en plus de sa règle d'insertion. Sur une table où les visiteurs peuvent seulement ajouter, c'est la règle de lecture qui l'arrête.","\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint\u002Fupsert-reads-three-rules-1600x660.png",[35,565,567],{"id":566},"peut-on-montrer-cette-erreur-à-vos-utilisateurs-sans-risque","Peut-on montrer cette erreur à vos utilisateurs sans risque ?",[10,569,570],{},"Non. Montrez une phrase à vous et gardez l'erreur brute dans vos logs, pour deux\nraisons : la ligne de détail peut renvoyer la valeur, et l'erreur confirme que la\nvaleur existe, quoi que vous affichiez.",[10,572,573,576,577,580,581,588,589,323],{},[395,574,575],{},"La ligne de détail répète la valeur."," ",[21,578,579],{},"Key (email)=(maria@example.com)"," est\nsans conséquence sur l'écran de Maria, et tout autre chose dans une capture\nd'écran postée sur un forum d'assistance, dans un outil de suivi d'erreurs, ou sur\nl'écran de quelqu'un qui a tapé l'adresse d'une autre personne dans votre\nformulaire. Postgres la protège déjà. La fonction qui construit la ligne\n(",[90,582,585],{"href":583,"rel":584},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Faccess\u002Findex\u002Fgenam.c",[94],[21,586,587],{},"BuildIndexValueDescription",")\nvérifie d'abord : \"if RLS is enabled for the relation. If so, return NULL to avoid\nleaking data.\" Une requête faite avec votre clé publique vers une table où la Row\nLevel Security est activée reçoit ",[21,590,591],{},"\"details\": null",[10,593,594,595,598,599,601,602,606],{},"Lue dans l'autre sens, la ligne vous renseigne sur la table. Si la console de\nvotre navigateur affiche ",[21,596,597],{},"Key (...)=(...)"," pour une requête que votre application\na faite avec la clé publique, la Row Level Security est désactivée sur cette table.\nLa seule exception est une requête que votre application envoie par du code\nserveur muni de la clé secrète, qui passe outre toutes les règles. ",[21,600,137],{}," prouve\nmoins de choses : Postgres retire aussi la ligne quand l'appelant ne peut pas lire\nchaque colonne de la clé.\n",[90,603,605],{"href":604},"\u002Fblog\u002Fenable-rls-on-every-supabase-table","Activer la Row Level Security sur chaque table","\ncouvre l'interrupteur et la première règle.",[10,608,609,610,614],{},"Une table dont les règles sont désactivées répond à un inconnu aussi volontiers\nqu'à vous. Notre ",[90,611,613],{"href":612},"\u002Fsecurity-scanner","scan gratuit"," lit votre application en ligne\navec la même clé publique qu'un visiteur, indique quelles tables rendent des\nlignes, et ne demande aucun compte.",[10,616,617,620,621,624,625,630],{},[395,618,619],{},"L'erreur répond à une question que vous ne voulez peut-être pas voir résolue.","\nMême sans la ligne de détail, un ",[21,622,623],{},"409"," apprend à l'expéditeur que la valeur est\ndéjà là. La\n",[90,626,629],{"href":627,"rel":628},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fddl-rowsecurity.html",[94],"page de Postgres sur la sécurité des lignes","\ndit que les contraintes uniques \"always bypass row security to ensure that data\nintegrity is maintained\", et vous demande de concevoir tables et règles pour que\nces vérifications ne deviennent pas un canal caché. Sur la liste d'attente,\nn'importe qui peut savoir si une adresse y figure en essayant de l'ajouter. Le\nvideur ne lit jamais le nom à voix haute, et quand il renvoie quelqu'un, la file\nsait quand même que le nom était là.",[10,632,633],{},"Si cela compte pour votre liste, donnez au navigateur la même réponse dans les\ndeux cas. Une fonction dans la base peut ajouter l'adresse, l'ignorer si elle est\ndéjà là, et ne rien renvoyer dans les deux cas :",[14,635,638],{"className":636,"code":637,"language":275,"meta":23},[273],"create function public.join_waitlist(new_email text)\nreturns void\nlanguage sql\nsecurity definer\nset search_path = ''\nas $$\n  insert into public.waitlist (email)\n  values (new_email)\n  on conflict (email) do nothing;\n$$;\n",[21,639,637],{"__ignoreMap":23},[10,641,642,643,646,647,650,651,656,657,661],{},"Votre application appelle ",[21,644,645],{},"supabase.rpc('join_waitlist', { new_email: email })",".\n",[21,648,649],{},"security definer"," exécute l'insert en tant que propriétaire de la fonction, les\nvisiteurs n'ont donc besoin d'aucune règle sur la table, et la règle qui les\nlaissait insérer peut partir, sinon l'ancien chemin reste ouvert à côté du nouveau.\n",[90,652,653,654],{"href":437},"Ce que \"Database error saving new user\" dit de ",[21,655,649],{},"\nexplique ce qu'il cède, et\n",[90,658,660],{"href":659},"\u002Fblog\u002Fsupabase-function-not-found","appeler une fonction Supabase"," montre comment\nfaire correspondre les noms d'arguments.",[380,663],{"alt":664,"caption":665,"src":666},"Le même doublon envoyé à deux tables. La table de gauche porte un cadenas, et sa réponse indique 23505 et 409 avec une ligne details vide. La table de droite n'en a pas, et sa réponse porte une ligne details en ambre qui dit Key (email)=(…).","Postgres n'écrit la valeur dans la ligne de détail que pour un appelant auquel la Row Level Security ne s'applique pas. La voir depuis le navigateur veut dire que les règles de la table sont désactivées.","\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint\u002Fthe-detail-line-and-rls-1600x700.png",[35,668,670],{"id":669},"avant-de-changer-la-façon-dont-votre-application-écrit","Avant de changer la façon dont votre application écrit",[10,672,673],{},"Gardez une copie de la base d'avant le moment où vous passez un insert en upsert\nou remettez un compteur à niveau, et soyez prévenu quand une table se met à\nrépondre à des inconnus.",[10,675,676],{},[395,677,678,682],{},[90,679,681],{"href":680},"\u002Fsupabase-backups","Reeve Care"," garde une copie de votre base de données Supabase :",[420,684,685,688,691,694,697,700,703],{},[423,686,687],{},"une copie chiffrée selon un calendrier, sur le stockage propre de Reeve dans l'UE, en dehors de votre compte Supabase",[423,689,690],{},"chaque table avec ses lignes telles qu'elles étaient avant qu'une fusion les écrase",[423,692,693],{},"le compteur d'id de chaque table revient avec ses lignes lors d'une restauration, pour que la ligne suivante reçoive un numéro libre",[423,695,696],{},"chaque copie vérifiée avant de compter, en comptant les lignes de chaque table",[423,698,699],{},"vos fichiers envoyés aussi, dès que vous connectez un identifiant Storage",[423,701,702],{},"une restauration en un clic, avec une copie de sécurité de l'état actuel prise d'abord",[423,704,705],{},"tout ce que fait Monitor",[10,707,708],{},[395,709,710,714],{},[90,711,713],{"href":712},"\u002Fwebsite-monitoring","Reeve Monitor"," continue de vérifier de l'extérieur :",[420,716,717,720,723,730],{},[423,718,719],{},"si votre application répond, vérifié chaque minute",[423,721,722],{},"la vérification de sécurité complète toutes les heures, sur trois applications au plus",[423,724,725,726,729],{},"un message quand un résultat ",[395,727,728],{},"change",", pour qu'une table qui se met à répondre à des inconnus après un déploiement vous parvienne sans que vous alliez voir",[423,731,732],{},"un rapport mensuel, en langage clair, de ce qu'il a vu",[35,734,736],{"id":735},"que-faire-maintenant","Que faire maintenant",[738,739,740],"key-takeaways",{},[420,741,742,751,760,766,772],{},[423,743,744,745,747,748,750],{},"Lisez le nom entre guillemets. La table et la colonne se trouvent avant ",[21,746,215],{},", et ",[21,749,239],{}," veut dire clé primaire.",[423,752,753,754,756,757,759],{},"Si le nom finit par ",[21,755,239],{}," et que votre application n'envoie pas d'id, remettez le compteur à niveau avec l'instruction ",[21,758,322],{}," ci-dessus.",[423,761,762,763,765],{},"Interceptez ",[21,764,81],{}," dans votre application et affichez une phrase que vous avez écrite, jamais le message brut.",[423,767,768,769,771],{},"N'utilisez upsert que là où la nouvelle ligne doit remplacer l'ancienne, avec ",[21,770,484],{}," réglé sur la colonne de l'erreur.",[423,773,774,775,777],{},"Si la console du navigateur affiche une ligne ",[21,776,597],{}," pour une requête faite avec votre clé publique, activez la Row Level Security sur cette table.",[10,779,780,781,783,784,787],{},"Commencez par le nom entre guillemets : il dit quelle règle a refusé la ligne et\nquelle colonne elle garde. Avant de lancer un ",[21,782,322],{},", un upsert ou toute autre\nchose qui réécrit des lignes dont des gens dépendent,\n",[90,785,786],{"href":680},"gardez une copie de la base"," d'avant le changement.",{"title":23,"searchDepth":789,"depth":789,"links":790},3,[791,793,794,795,796,797,798,799],{"id":37,"depth":792,"text":38},2,{"id":182,"depth":792,"text":183},{"id":294,"depth":792,"text":295},{"id":387,"depth":792,"text":388},{"id":445,"depth":792,"text":446},{"id":566,"depth":792,"text":567},{"id":669,"depth":792,"text":670},{"id":735,"depth":792,"text":736},"Bases de la sécurité","\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint\u002Fcover-1200x630.png","Une liste de noms sur un porte-bloc, une ligne en turquoise, et devant elle un papier au même nom en ambre, tenu à l'écart.","Supabase \"duplicate key value violates unique constraint\" : la valeur est déjà prise. Trouver la colonne, choisir un correctif, garder la valeur hors écran.",false,"md",[807,810,812,814,817],{"q":808,"a":809},"Que signifie duplicate key value violates unique constraint ?","Postgres a refusé d'ajouter ou de modifier une ligne parce qu'une autre ligne contient déjà la même valeur dans une colonne, ou un groupe de colonnes, qui n'accepte chaque valeur qu'une fois. Rien n'a été écrit. Le code est 23505, et depuis une application Supabase il arrive en HTTP 409.",{"q":183,"a":811},"Le nom de la règle entre guillemets vous le dit. Postgres nomme une contrainte unique d'après sa table et ses colonnes, avec _key à la fin : waitlist_email_key est donc la colonne email de la table waitlist, et un nom en _pkey est la clé primaire, en général id. La ligne de détail, quand elle est là, nomme la colonne et la valeur.",{"q":446,"a":813},"Seulement quand la nouvelle ligne doit remplacer l'ancienne, comme une ligne de réglages par utilisateur. Réglez onConflict sur la colonne de l'erreur, car les upserts Supabase vérifient la clé primaire par défaut. Depuis le navigateur, un upsert a aussi besoin de règles de lecture que la ligne respecte : sur une table où les visiteurs peuvent seulement ajouter, il échoue, ou il vous oblige à ouvrir la table en lecture.",{"q":815,"a":816},"Pourquoi cela arrive-t-il à l'inscription ?","Deux chemins y mènent : un trigger copie chaque nouvel utilisateur dans une table à vous et la valeur copiée y est déjà, ou votre application insère la même ligne de profil que le trigger a déjà créée. Supabase signale l'un comme l'autre par \"Database error saving new user\", et le 23505 lui-même se trouve dans les logs Postgres.",{"q":818,"a":819},"Peut-on montrer cette erreur aux utilisateurs sans risque ?","Non. La ligne de détail peut répéter la valeur, le message nomme votre table et votre colonne, et l'erreur elle-même apprend à l'expéditeur que la valeur existe. Interceptez le code 23505, affichez une phrase que vous avez écrite, et gardez l'erreur brute dans vos propres logs.","\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint\u002Fcard-800x500.png",[822,823,824,825,826,827,828,829],"duplicate key value violates unique constraint","supabase erreur clé dupliquée","postgres 23505","supabase upsert doublon","key already exists","duplicate key value violates unique constraint pkey","there is no unique or exclusion constraint matching the on conflict specification","supabase onconflict",{},true,"\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint","2026-10-24",{"title":5,"description":803},"blog\u002Fduplicate-key-value-violates-unique-constraint",[837,838,839],"Supabase \"duplicate key value violates unique constraint\" (code Postgres 23505, HTTP 409 dans votre application) signifie qu'une ligne contient déjà cette valeur dans une colonne qui n'accepte chaque valeur qu'une fois. Le nom entre guillemets dit quelle colonne.","\"Key (id)=(1) already exists\" alors que votre application n'envoie aucun id signifie que le compteur d'id de la table est en retard sur ses lignes. Une ligne setval le remet d'aplomb.","Un upsert n'aide que si onConflict nomme cette colonne, et depuis le navigateur il lui faut aussi les règles de lecture de la table. Montrez à vos utilisateurs une phrase à vous : le message brut peut renvoyer la valeur.","zpOiYBcgdwEjdI7TnDorBJtSGKj_pDxbyfzcRbqLWGM",[842,848,854,860,866,872,878,884,890,896,902,909,915,921,927,933,939,940,945,950,956,962,968,974,980,986,992,997,1003,1009,1015,1021,1027,1033,1039,1045,1051,1057,1063,1069,1075,1081,1087,1093,1099,1105,1110,1116,1122,1128,1134,1140,1146,1152,1158,1164,1170,1176,1182,1188,1194,1200,1205,1211,1217,1223,1229,1235,1241,1247,1253,1259,1265,1271,1277,1283,1288,1294,1300,1306,1312,1318,1324,1330,1335,1341,1347,1353,1358,1364],{"path":843,"title":844,"description":845,"published":846,"category":800,"image":847,"draft":804},"\u002Fblog\u002Fmax-client-connections-reached","Supabase \"max client connections reached\" expliqué","Supabase \"max client connections reached\" : le pooler n'a plus de place pour un client de plus. Le port de votre chaîne de connexion fixe la limite.","2026-11-09","\u002Fblog\u002Fmax-client-connections-reached\u002Fcard-800x500.png",{"path":849,"title":850,"description":851,"published":852,"category":800,"image":853,"draft":804},"\u002Fblog\u002Funsupported-provider-provider-is-not-enabled","Erreur Supabase : \"Unsupported provider: provider is not enabled\"","\"Unsupported provider: provider is not enabled\" : la connexion Google est désactivée dans votre projet Supabase. Comment l'activer, et redirect_uri_mismatch.","2026-11-08","\u002Fblog\u002Funsupported-provider-provider-is-not-enabled\u002Fcard-800x500.png",{"path":855,"title":856,"description":857,"published":858,"category":800,"image":859,"draft":804},"\u002Fblog\u002Fviolates-foreign-key-constraint","Erreur Supabase \"violates foreign key constraint\" à l'insertion","Supabase \"insert or update on table violates foreign key constraint\" : votre ligne pointe vers une ligne absente. Laquelle, pourquoi, et la solution.","2026-11-07","\u002Fblog\u002Fviolates-foreign-key-constraint\u002Fcard-800x500.png",{"path":861,"title":862,"description":863,"published":864,"category":800,"image":865,"draft":804},"\u002Fblog\u002Fnull-value-violates-not-null-constraint","Erreur Supabase \"null value in column violates not-null constraint\"","Supabase \"null value in column violates not-null constraint\" : une colonne obligatoire est arrivée vide. D'où vient le null, et la solution.","2026-11-06","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fcard-800x500.png",{"path":867,"title":868,"description":869,"published":870,"category":800,"image":871,"draft":804},"\u002Fblog\u002Fauth-session-missing","Supabase \"Auth session missing!\" : où est passée la session","\"Auth session missing!\" : supabase-js n'a trouvé aucune session connectée et n'a même pas interrogé Supabase. Où est passée la session, et la solution.","2026-11-05","\u002Fblog\u002Fauth-session-missing\u002Fcard-800x500.png",{"path":873,"title":874,"description":875,"published":876,"category":800,"image":877,"draft":804},"\u002Fblog\u002Finvalid-claim-missing-sub-claim","Supabase \"invalid claim: missing sub claim\" : personne de connecté","\"invalid claim: missing sub claim\" : Supabase Auth a reçu un vrai jeton qui ne nomme personne, la clé du projet à la place d'une session.","2026-11-04","\u002Fblog\u002Finvalid-claim-missing-sub-claim\u002Fcard-800x500.png",{"path":879,"title":880,"description":881,"published":882,"category":800,"image":883,"draft":804},"\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function","\"Failed to send a request to the Edge Function\" : la vraie cause","\"Failed to send a request to the Edge Function\" : le navigateur n'a reçu aucune réponse lisible. Sa console dit pourquoi, et si votre fonction a tourné.","2026-11-03","\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function\u002Fcard-800x500.png",{"path":885,"title":886,"description":887,"published":888,"category":800,"image":889,"draft":804},"\u002Fblog\u002Fsupabase-auth-redirects-to-localhost","Pourquoi les liens e-mail de Supabase redirigent vers localhost:3000","Si Supabase redirige vers localhost:3000, la Site URL n'a jamais été changée ou votre adresse manque dans Redirect URLs. La solution, et le joker à éviter.","2026-11-02","\u002Fblog\u002Fsupabase-auth-redirects-to-localhost\u002Fcard-800x500.png",{"path":891,"title":892,"description":893,"published":894,"category":800,"image":895,"draft":804},"\u002Fblog\u002Femail-link-is-invalid-or-has-expired","Supabase \"Email link is invalid or has expired\" sur un lien neuf","Sur un lien envoyé il y a une minute, \"Email link is invalid or has expired\" de Supabase signifie souvent qu'un filtre de messagerie l'a utilisé avant.","2026-11-01","\u002Fblog\u002Femail-link-is-invalid-or-has-expired\u002Fcard-800x500.png",{"path":897,"title":898,"description":899,"published":900,"category":800,"image":901,"draft":804},"\u002Fblog\u002Fcanceling-statement-due-to-statement-timeout","Supabase \"canceling statement due to statement timeout\" expliqué","Supabase \"canceling statement due to statement timeout\" : une requête a dépassé la limite de son rôle, 3 secondes pour un visiteur. Vérifiez la règle RLS.","2026-10-31","\u002Fblog\u002Fcanceling-statement-due-to-statement-timeout\u002Fcard-800x500.png",{"path":903,"title":904,"description":905,"published":906,"category":907,"image":908,"draft":804},"\u002Fblog\u002Fdatabase-error-deleting-user","\"Database error deleting user\" sur Supabase, et ce qui part avec","\"Database error deleting user\" sur Supabase : votre base a refusé la suppression. La cause est dans vos logs, et la solution décide de ce qui part avec.","2026-10-30","Backups","\u002Fblog\u002Fdatabase-error-deleting-user\u002Fcard-800x500.png",{"path":910,"title":911,"description":912,"published":913,"category":800,"image":914,"draft":804},"\u002Fblog\u002Fstripe-webhook-no-signatures-found","\"No signatures found matching the expected signature\" sur Supabase","Un webhook Stripe dans une Edge Function Supabase affiche \"No signatures found matching the expected signature\" ? Deux causes. Gardez la vérification.","2026-10-29","\u002Fblog\u002Fstripe-webhook-no-signatures-found\u002Fcard-800x500.png",{"path":916,"title":917,"description":918,"published":919,"category":800,"image":920,"draft":804},"\u002Fblog\u002Fsupabase-invalid-login","\"Invalid login credentials\" dans Supabase : les trois causes","Supabase répond \"Invalid login credentials\" à un mauvais mot de passe, un e-mail inconnu et un compte Google. Comment savoir lequel.","2026-10-28","\u002Fblog\u002Fsupabase-invalid-login\u002Fcard-800x500.png",{"path":922,"title":923,"description":924,"published":925,"category":800,"image":926,"draft":804},"\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code","\"Edge Function returned a non-2xx status code\" : la vraie erreur","\"Edge Function returned a non-2xx status code\" signifie que votre fonction Supabase a répondu par une erreur. Voici où lire la vraie.","2026-10-27","\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code\u002Fcard-800x500.png",{"path":928,"title":929,"description":930,"published":931,"category":800,"image":932,"draft":804},"\u002Fblog\u002Fcertificate-not-trusted","Certificat non approuvé : trois causes autres que l’expiration","Certificat non approuvé alors que la date est bonne ? Il nomme une autre adresse, il s’est signé lui-même ou il manque un maillon à sa chaîne.","2026-10-26","\u002Fblog\u002Fcertificate-not-trusted\u002Fcard-800x500.png",{"path":934,"title":935,"description":936,"published":937,"category":800,"image":938,"draft":804},"\u002Fblog\u002Finvalid-input-syntax-for-type-uuid","Supabase \"invalid input syntax for type uuid\" : lisez les guillemets","Supabase \"invalid input syntax for type uuid\" : votre app a envoyé autre chose qu'un id. Lisez les guillemets, retenez la requête, gardez la colonne.","2026-10-25","\u002Fblog\u002Finvalid-input-syntax-for-type-uuid\u002Fcard-800x500.png",{"path":832,"title":5,"description":803,"published":833,"category":800,"image":820,"draft":804},{"path":364,"title":941,"description":942,"published":943,"category":800,"image":944,"draft":804},"Supabase \"relation does not exist\" alors que la table existe","Supabase \"relation does not exist\" ou PGRST205 : aucune table ne porte ce nom dans ce schéma. Quatre vérifications, et ce qu'ouvre un schéma exposé.","2026-10-23","\u002Fblog\u002Fsupabase-relation-does-not-exist\u002Fcard-800x500.png",{"path":659,"title":946,"description":947,"published":948,"category":800,"image":949,"draft":804},"Supabase PGRST202: \"Could not find the function in the schema cache\"","Supabase \"Could not find the function in the schema cache\" cite les noms d'arguments envoyés par votre application. Comparez-les à la fonction.","2026-10-22","\u002Fblog\u002Fsupabase-function-not-found\u002Fcard-800x500.png",{"path":951,"title":952,"description":953,"published":954,"category":800,"image":955,"draft":804},"\u002Fblog\u002Fsupabase-schema-cache-column-error","Supabase PGRST204: \"Could not find the column in the schema cache\"","Supabase \"Could not find the column in the schema cache\" vient le plus souvent du nom. Quatre vérifications, et la ligne qui recharge le cache.","2026-10-21","\u002Fblog\u002Fsupabase-schema-cache-column-error\u002Fcard-800x500.png",{"path":957,"title":958,"description":959,"published":960,"category":800,"image":961,"draft":804},"\u002Fblog\u002Fsupabase-email-rate-limit-exceeded","Supabase \"Email Rate Limit Exceeded\" : deux limites, un code","Supabase \"Email Rate Limit Exceeded\" plafonne chaque e-mail de votre projet : 2 par heure avec l'expéditeur intégré. Ce qui compte, et comment le relever.","2026-10-20","\u002Fblog\u002Fsupabase-email-rate-limit-exceeded\u002Fcard-800x500.png",{"path":963,"title":964,"description":965,"published":966,"category":800,"image":967,"draft":804},"\u002Fblog\u002Fsupabase-users-logged-out","Supabase \"Invalid Refresh Token: Refresh Token Not Found\" : 3 causes","Supabase \"Invalid Refresh Token: Refresh Token Not Found\" déconnecte vos utilisateurs. Trois causes, et comment le log Auth les distingue.","2026-10-19","\u002Fblog\u002Fsupabase-users-logged-out\u002Fcard-800x500.png",{"path":969,"title":970,"description":971,"published":972,"category":800,"image":973,"draft":804},"\u002Fblog\u002Fpermission-denied-for-schema-public","Supabase \"permission denied for schema public\" : trois causes","Supabase \"permission denied for schema public\" signale un grant manquant sur le schéma. Ce qui l'a retiré, et la ligne qui le remet.","2026-10-18","\u002Fblog\u002Fpermission-denied-for-schema-public\u002Fcard-800x500.png",{"path":975,"title":976,"description":977,"published":978,"category":800,"image":979,"draft":804},"\u002Fblog\u002Fsupabase-bucket-not-found","Supabase \"Bucket not found\" : les trois choses que cela veut dire","Supabase \"Bucket not found\", c'est une de trois choses : un lien public vers un bucket privé, un nom pas tout à fait exact, ou le mauvais projet.","2026-10-17","\u002Fblog\u002Fsupabase-bucket-not-found\u002Fcard-800x500.png",{"path":981,"title":982,"description":983,"published":984,"category":800,"image":985,"draft":804},"\u002Fblog\u002Fsupabase-invalid-jwt","Supabase invalid JWT : quelle clé a cessé de marcher ?","Une erreur Supabase invalid JWT, c'est PGRST301 : un jeton signé n'a pas passé la vérification. Sur un projet récent, c'est la session, pas la clé API.","2026-10-16","\u002Fblog\u002Fsupabase-invalid-jwt\u002Fcard-800x500.png",{"path":987,"title":988,"description":989,"published":990,"category":800,"image":991,"draft":804},"\u002Fblog\u002Fsupabase-single-row-error","Supabase \"multiple (or no) rows returned\" veut dire zéro ligne","Supabase renvoie PGRST116, \"JSON object requested, multiple (or no) rows returned\", quand la requête n'a rien trouvé. Une ligne cachée semble absente.","2026-10-15","\u002Fblog\u002Fsupabase-single-row-error\u002Fcard-800x500.png",{"path":437,"title":993,"description":994,"published":995,"category":800,"image":996,"draft":804},"\"Database error saving new user\" sur Supabase : où regarder","\"Database error saving new user\" veut dire que quelque chose rattaché à votre inscription Supabase a échoué. La vraie erreur est dans vos logs.","2026-10-14","\u002Fblog\u002Fdatabase-error-saving-new-user\u002Fcard-800x500.png",{"path":998,"title":999,"description":1000,"published":1001,"category":800,"image":1002,"draft":804},"\u002Fblog\u002Fwhat-is-supabase","Qu'est-ce que Supabase ? La base que votre builder a choisie","Qu'est-ce que Supabase : la base de données, la connexion et le stockage de fichiers de votre app. Les règles sur qui lit vos lignes sont à vous.","2026-10-13","\u002Fblog\u002Fwhat-is-supabase\u002Fcard-800x500.png",{"path":1004,"title":1005,"description":1006,"published":1007,"category":800,"image":1008,"draft":804},"\u002Fblog\u002Faws-access-key-exposed-in-frontend","Une clé AWS exposée dans le frontend n'est pas une clé OpenAI","Une clé d'accès AWS exposée dans votre frontend lit et écrit votre stockage. La solution est en général une presigned URL, pas une nouvelle clé.","2026-10-12","\u002Fblog\u002Faws-access-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":1010,"title":1011,"description":1012,"published":1013,"category":800,"image":1014,"draft":804},"\u002Fblog\u002Fstorage-upload-violates-row-level-security","\"Row-level security policy for table objects\" à l'upload","\"New row violates row-level security policy for table objects\" signifie que votre upload n'a pas de règle insert. Rendre le bucket public n'en ajoute aucune.","2026-10-11","\u002Fblog\u002Fstorage-upload-violates-row-level-security\u002Fcard-800x500.png",{"path":1016,"title":1017,"description":1018,"published":1019,"category":800,"image":1020,"draft":804},"\u002Fblog\u002Fbase44-security-scan","Scan de sécurité Base44 : ce que lui seul peut voir","Le scan de sécurité Base44 vérifie sept types de problèmes depuis l’intérieur. Voici la moitié que lui seul lit, et celle qu’il ne regarde jamais.","2026-10-10","\u002Fblog\u002Fbase44-security-scan\u002Fcard-800x500.png",{"path":1022,"title":1023,"description":1024,"published":1025,"category":800,"image":1026,"draft":804},"\u002Fblog\u002Fapi-key-leaked-what-to-do","Votre clé API a fuité. L'ordre dans lequel agir","Une clé API a fuité et vous ne savez pas par quoi commencer. Toutes les clés de votre frontend n'en sont pas une, et l'ordre compte plus que la vitesse.","2026-10-09","\u002Fblog\u002Fapi-key-leaked-what-to-do\u002Fcard-800x500.png",{"path":1028,"title":1029,"description":1030,"published":1031,"category":800,"image":1032,"draft":804},"\u002Fblog\u002Finfinite-recursion-in-policy-for-relation","\"Infinite recursion detected in policy\" sans désactiver RLS","\"Infinite recursion detected in policy for relation\" signifie que votre policy a interrogé la table qu'elle protège. Voici comment rompre le cercle.","2026-10-08","\u002Fblog\u002Finfinite-recursion-in-policy-for-relation\u002Fcard-800x500.png",{"path":1034,"title":1035,"description":1036,"published":1037,"category":800,"image":1038,"draft":804},"\u002Fblog\u002Fno-api-key-found-in-request","\"No API key found in request\" sur Supabase, et le mauvais correctif","\"No API key found in request\" veut dire que votre requête est arrivée sans clé. La plupart des réponses pointent vers les règles de votre base.","2026-10-07","\u002Fblog\u002Fno-api-key-found-in-request\u002Fcard-800x500.png",{"path":1040,"title":1041,"description":1042,"published":1043,"category":800,"image":1044,"draft":804},"\u002Fblog\u002Fis-base44-safe","Base44 est-il sûr ? Ce que 5 442 applications ont montré","Base44 est-il sûr ? Neuf vérifications sur 5 442 applications Base44 en ligne. Presque toutes ont eu un B, pour des raisons que personne n’a choisies.","2026-10-06","\u002Fblog\u002Fis-base44-safe\u002Fcard-800x500.png",{"path":1046,"title":1047,"description":1048,"published":1049,"category":800,"image":1050,"draft":804},"\u002Fblog\u002Fmove-a-secret-to-an-edge-function","Cacher une clé API : la déplacer dans une Edge Function Supabase","Cacher une clé API veut dire la sortir du navigateur, et une Edge Function Supabase est le plus petit endroit où la mettre. Deux étapes comptent davantage.","2026-10-05","\u002Fblog\u002Fmove-a-secret-to-an-edge-function\u002Fcard-800x500.png",{"path":1052,"title":1053,"description":1054,"published":1055,"category":800,"image":1056,"draft":804},"\u002Fblog\u002Fenv-file-exposed-on-your-server","Votre fichier .env est-il exposé ? Les douze chemins à vérifier","Votre fichier .env est-il exposé sur votre propre serveur web ? Douze adresses vous le disent, et une réponse signifie que tout son contenu est public.","2026-10-04","\u002Fblog\u002Fenv-file-exposed-on-your-server\u002Fcard-800x500.png",{"path":1058,"title":1059,"description":1060,"published":1061,"category":800,"image":1062,"draft":804},"\u002Fblog\u002Fis-v0-safe","Sécurité de v0 : les 1 790 applications scannées ont toutes eu A","La sécurité de v0 mesurée sur 1 790 applications en ligne : toutes ont eu A. Seules 17 citaient une base de données, et c’est surtout cela que ce A mesure.","2026-10-03","\u002Fblog\u002Fis-v0-safe\u002Fcard-800x500.png",{"path":1064,"title":1065,"description":1066,"published":1067,"category":800,"image":1068,"draft":804},"\u002Fblog\u002Fis-bolt-safe","Bolt est-il sûr ? Ce que 1 123 applications Bolt ont montré","Bolt est-il sûr ? Neuf vérifications sur 1 123 applications Bolt en ligne. L’hébergement était propre. Les trouvailles : des clés et des tables ouvertes.","2026-10-02","\u002Fblog\u002Fis-bolt-safe\u002Fcard-800x500.png",{"path":1070,"title":1071,"description":1072,"published":1073,"category":800,"image":1074,"draft":804},"\u002Fblog\u002Fis-supabase-down-or-your-app","Supabase en panne, ou est-ce votre app ? Comment le savoir","Supabase est-il en panne, ou est-ce votre app ? Un projet en pause, une base pleine et un bug chez vous se ressemblent. Quatre vérifications les séparent.","2026-10-01","\u002Fblog\u002Fis-supabase-down-or-your-app\u002Fcard-800x500.png",{"path":1076,"title":1077,"description":1078,"published":1079,"category":800,"image":1080,"draft":804},"\u002Fblog\u002Fdoes-supabase-encrypt-my-data","Supabase chiffre-t-il mes données ? Oui. Ce que cela arrête","Supabase chiffre-t-il les données ? Oui : AES-256 au repos, TLS en transit, SOC 2 et ISO 27001. Ce que couvre chacun, et la fuite qu'aucun n'arrête.","2026-09-30","\u002Fblog\u002Fdoes-supabase-encrypt-my-data\u002Fcard-800x500.png",{"path":1082,"title":1083,"description":1084,"published":1085,"category":800,"image":1086,"draft":804},"\u002Fblog\u002Fsupabase-free-plan-limits","Limites du plan gratuit Supabase : ce qui se passe à chacune","Les limites du plan gratuit Supabase : 500 Mo de base, 5 Go d'egress, aucune sauvegarde. Ce qui se passe à chaque seuil, et ce que change Pro à $25.","2026-09-29","\u002Fblog\u002Fsupabase-free-plan-limits\u002Fcard-800x500.png",{"path":1088,"title":1089,"description":1090,"published":1091,"category":907,"image":1092,"draft":804},"\u002Fblog\u002Ftest-your-supabase-backup","Testez votre sauvegarde Supabase avant d'en avoir besoin","Comment tester votre sauvegarde Supabase : la restaurer dans un projet d'exercice, compter les lignes, se connecter, chercher la ligne qui manque.","2026-09-28","\u002Fblog\u002Ftest-your-supabase-backup\u002Fcard-800x500.png",{"path":1094,"title":1095,"description":1096,"published":1097,"category":907,"image":1098,"draft":804},"\u002Fblog\u002Fsupabase-backup-github-action","Sauvegarde Supabase gratuite avec une GitHub Action, et le piège","Une sauvegarde Supabase par GitHub Action est gratuite et convient à beaucoup d'apps. Le workflow, la bonne connection string et l'egress de chaque run.","2026-09-27","\u002Fblog\u002Fsupabase-backup-github-action\u002Fcard-800x500.png",{"path":1100,"title":1101,"description":1102,"published":1103,"category":907,"image":1104,"draft":804},"\u002Fblog\u002Fdownload-your-supabase-backup","Pourquoi vous ne pouvez pas télécharger votre sauvegarde Supabase","Impossible de télécharger votre sauvegarde Supabase sur un projet récent : la copie quotidienne est physique. Comment le vérifier et garder la vôtre.","2026-09-26","\u002Fblog\u002Fdownload-your-supabase-backup\u002Fcard-800x500.png",{"path":1106,"title":1107,"description":1108,"published":1103,"category":800,"image":1109,"draft":804},"\u002Fblog\u002Fsupabase-permission-denied-for-table","Supabase \"permission denied for table\" : le grant manquant","Dès le 30 octobre, une nouvelle table Supabase répond \"permission denied for table\" tant que l'accès n'est pas accordé. Le grant de l'e-mail est la moitié.","\u002Fblog\u002Fsupabase-permission-denied-for-table\u002Fcard-800x500.png",{"path":1111,"title":1112,"description":1113,"published":1114,"category":800,"image":1115,"draft":804},"\u002Fblog\u002Fopen-api-endpoint-exposed","Un endpoint d'API ouvert est-il un problème de sécurité ?","Votre scan a signalé un endpoint d'API ouvert. Que cela compte dépend de ce qui est revenu, et la plupart de ceux trouvés étaient à la plateforme.","2026-09-25","\u002Fblog\u002Fopen-api-endpoint-exposed\u002Fcard-800x500.png",{"path":1117,"title":1118,"description":1119,"published":1120,"category":800,"image":1121,"draft":804},"\u002Fblog\u002Flovable-security-scan","Scan de sécurité Lovable : ce qu’il ne peut pas prouver","Scan de sécurité Lovable : ce que vérifient le Quick scan et le Deep scan, quand chacun part, et la seule chose qu’un scan de l’intérieur ne prouve pas.","2026-09-24","\u002Fblog\u002Flovable-security-scan\u002Fcard-800x500.png",{"path":1123,"title":1124,"description":1125,"published":1126,"category":907,"image":1127,"draft":804},"\u002Fblog\u002Fsupabase-backup-auth-users","Pourquoi votre dump Supabase ne contient aucun utilisateur","Lancez supabase db dump tout seul et vous obtenez la forme de votre base et aucune de ses lignes, sans le schéma auth où vivent vos utilisateurs.","2026-09-23","\u002Fblog\u002Fsupabase-backup-auth-users\u002Fcard-800x500.png",{"path":1129,"title":1130,"description":1131,"published":1132,"category":800,"image":1133,"draft":804},"\u002Fblog\u002Fdomain-and-certificate-expiry","Domaine expiré, site hors ligne : ce qui se passe ensuite","Votre domaine a expiré et votre site est hors ligne. Le compte à rebours qui démarre, pourquoi le certificat est le moindre souci, et comment vérifier les deux.","2026-09-22","\u002Fblog\u002Fdomain-and-certificate-expiry\u002Fcard-800x500.png",{"path":1135,"title":1136,"description":1137,"published":1138,"category":800,"image":1139,"draft":804},"\u002Fblog\u002Fis-lovable-safe","Lovable est-il sûr ? Ce que 18 554 applications Lovable ont montré","Lovable est-il sûr ? 18 554 applications, neuf vérifications. La plateforme était la plus propre des cinq. Les trouvailles étaient dans les applications.","2026-09-21","\u002Fblog\u002Fis-lovable-safe\u002Fcard-800x500.png",{"path":1141,"title":1142,"description":1143,"published":1144,"category":800,"image":1145,"draft":804},"\u002Fblog\u002Fvibe-coded-app-security-checklist","La checklist de sécurité du vibe coding, en neuf vérifications","Une checklist de sécurité du vibe coding en neuf points, chacun vérifiable de l’extérieur sur votre appli en ligne, et chacun avec un test d’une ligne.","2026-09-20","\u002Fblog\u002Fvibe-coded-app-security-checklist\u002Fcard-800x500.png",{"path":1147,"title":1148,"description":1149,"published":1150,"category":800,"image":1151,"draft":804},"\u002Fblog\u002Fstripe-secret-key-in-frontend","Une clé secrète Stripe dans votre frontend peut déplacer de l’argent","Une clé secrète Stripe exposée dans votre frontend peut rembourser, débiter et lire chaque fiche client que vous détenez. Votre clé pk_live_ y a sa place.","2026-09-19","\u002Fblog\u002Fstripe-secret-key-in-frontend\u002Fcard-800x500.png",{"path":1153,"title":1154,"description":1155,"published":1156,"category":800,"image":1157,"draft":804},"\u002Fblog\u002Fvite-and-next-public-env-vars","Variables d’environnement Vite exposées : le préfixe dit publier","Variables d’environnement Vite exposées : votre appli a fait ce que le préfixe demandait. VITE_ et NEXT_PUBLIC_ veulent dire publier, et l’IA ignorait le prix.","2026-09-18","\u002Fblog\u002Fvite-and-next-public-env-vars\u002Fcard-800x500.png",{"path":1159,"title":1160,"description":1161,"published":1162,"category":800,"image":1163,"draft":804},"\u002Fblog\u002Fbase44-source-maps","Sécurité Base44 : ce qu’un scan signale, et ce qui dépend de vous","Sécurité Base44 sur 5 438 applis : trois signalements sur presque chacune sont ceux de la plateforme, la source map est le badge Base44, votre part est courte.","2026-09-17","\u002Fblog\u002Fbase44-source-maps\u002Fcard-800x500.png",{"path":1165,"title":1166,"description":1167,"published":1168,"category":800,"image":1169,"draft":804},"\u002Fblog\u002Fis-cursor-ai-safe","Cursor AI est-il sûr ? L’éditeur, le code et l’appli publiée","Cursor AI est-il sûr ? Trois questions dans une recherche : ce que Cursor garde, ce que son code rate, et si l’appli que vous avez publiée est ouverte.","2026-09-16","\u002Fblog\u002Fis-cursor-ai-safe\u002Fcard-800x500.png",{"path":1171,"title":1172,"description":1173,"published":1174,"category":800,"image":1175,"draft":804},"\u002Fblog\u002Fis-replit-safe","Replit est-il sûr ? Ce que 3 042 applications Replit nous ont montré","Replit est-il sûr ? 3 042 applications Replit en ligne, neuf vérifications externes. Les trouvailles étaient dans l’application publiée, pas chez l’hébergeur.","2026-09-15","\u002Fblog\u002Fis-replit-safe\u002Fcard-800x500.png",{"path":1177,"title":1178,"description":1179,"published":1180,"category":907,"image":1181,"draft":804},"\u002Fblog\u002Fsupabase-storage-backup","Supabase storage backup : votre copie de base n'a aucun fichier","Un Supabase storage backup est une tâche à part. Les sauvegardes de la base gardent la liste de vos fichiers et aucun des fichiers, donc chaque upload casse.","2026-09-14","\u002Fblog\u002Fsupabase-storage-backup\u002Fcard-800x500.png",{"path":1183,"title":1184,"description":1185,"published":1186,"category":907,"image":1187,"draft":804},"\u002Fblog\u002Fsupabase-point-in-time-recovery","Point-in-time recovery Supabase : prix et limites","Le point-in-time recovery de Supabase rembobine votre base à la seconde près sur la dernière semaine. $100 par mois en plus de Pro, et rien que la base.","2026-09-13","\u002Fblog\u002Fsupabase-point-in-time-recovery\u002Fcard-800x500.png",{"path":1189,"title":1190,"description":1191,"published":1192,"category":907,"image":1193,"draft":804},"\u002Fblog\u002Fsupabase-project-paused-recover","Projet Supabase en pause ? Vos données sont toujours là","Supabase a mis votre projet en pause après une semaine sans activité. Rien n'est supprimé, Restaurer est à côté du nom, et vous avez un an pour cliquer.","2026-09-12","\u002Fblog\u002Fsupabase-project-paused-recover\u002Fcard-800x500.png",{"path":1195,"title":1196,"description":1197,"published":1198,"category":800,"image":1199,"draft":804},"\u002Fblog\u002Fsafest-ai-app-builder","Quel builder d'app IA est le plus sûr ? 30 998 apps testées","Quel builder d'app IA est le plus sûr ? Nous avons scanné 30 998 apps actives de Lovable, Base44, Replit, v0 et Bolt. Ce n'est pas le builder qui décide.","2026-09-11","\u002Fblog\u002Fsafest-ai-app-builder\u002Fcard-800x500.png",{"path":604,"title":1201,"description":1202,"published":1203,"category":800,"image":1204,"draft":804},"Activer Row Level Security sur chaque table Supabase, et le prouver","Activer Row Level Security dans Supabase sans politique ferme la table entièrement. Une politique sans le réglage ne fait rien. Voici le SQL, et le test.","2026-09-10","\u002Fblog\u002Fenable-rls-on-every-supabase-table\u002Fcard-800x500.png",{"path":1206,"title":1207,"description":1208,"published":1209,"category":800,"image":1210,"draft":804},"\u002Fblog\u002Fsupabase-rls-disabled-in-public","Supabase \"RLS disabled in public\" : ce que l'alerte rate","Supabase signale \"RLS disabled in public\" comme une erreur. Elle ne dit rien de la politique de lecture qui laisse votre table tout aussi ouverte.","2026-09-09","\u002Fblog\u002Fsupabase-rls-disabled-in-public\u002Fcard-800x500.png",{"path":1212,"title":1213,"description":1214,"published":1215,"category":800,"image":1216,"draft":804},"\u002Fblog\u002Frotate-supabase-service-role-key","Faire tourner une clé service_role Supabase qui a fuité","Supabase dit de colmater la fuite d'abord. D'autres guides disent de faire tourner la clé tout de suite. Tout dépend d'où votre clé service_role a fuité.","2026-09-08","\u002Fblog\u002Frotate-supabase-service-role-key\u002Fcard-800x500.png",{"path":1218,"title":1219,"description":1220,"published":1221,"category":800,"image":1222,"draft":804},"\u002Fblog\u002Fvibe-coding-security-scanners-compared","Scanners de sécurité vibe coding comparés, le nôtre compris","Le meilleur scanner de sécurité vibe coding tient à trois questions qu'aucune liste de fonctions ne traite. Huit outils comparés, avec les prix.","2026-09-07","\u002Fblog\u002Fvibe-coding-security-scanners-compared\u002Fcard-800x500.png",{"path":1224,"title":1225,"description":1226,"published":1227,"category":800,"image":1228,"draft":804},"\u002Fblog\u002Fsupabase-security-checker","Supabase security checker : les cinq vérifications à faire soi-même","Un Supabase security checker lit votre app publiée plutôt que les réglages de votre projet. Voici les cinq vérifications, et comment faire chacune vous-même.","2026-09-06","\u002Fblog\u002Fsupabase-security-checker\u002Fcard-800x500.png",{"path":1230,"title":1231,"description":1232,"published":1233,"category":800,"image":1234,"draft":804},"\u002Fblog\u002Fvibe-coding-security-scanner","Scanner de sécurité vibe coding : ce qu’un scan d’URL rate","Un scanner de sécurité vibe coding lit votre app en ligne depuis l’extérieur. Ce qu’il couvre, les quatre choses qu’il ne voit pas, et comment lire le résultat.","2026-09-05","\u002Fblog\u002Fvibe-coding-security-scanner\u002Fcard-800x500.png",{"path":1236,"title":1237,"description":1238,"published":1239,"category":800,"image":1240,"draft":804},"\u002Fblog\u002Fmissing-security-headers","En-têtes de sécurité manquants : quand cela compte","Les en-têtes de sécurité manquants sont notre constat le plus fréquent. Contre quoi ils protègent, et quand ils comptent le moins dans votre rapport.","2026-09-04","\u002Fblog\u002Fmissing-security-headers\u002Fcard-800x500.png",{"path":1242,"title":1243,"description":1244,"published":1245,"category":800,"image":1246,"draft":804},"\u002Fblog\u002Fopenai-api-key-exposed-in-frontend","Votre clé API OpenAI est exposée dans votre frontend. Changez-la.","Une clé API OpenAI exposée dans votre frontend ne peut être liée à aucun domaine. Changez-la aujourd'hui, passez par votre serveur et plafonnez la dépense.","2026-09-03","\u002Fblog\u002Fopenai-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":1248,"title":1249,"description":1250,"published":1251,"category":800,"image":1252,"draft":804},"\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps","Clé API exposée dans le frontend : ce que 30 998 applis livrent","Une clé API exposée dans votre frontend est le plus souvent une clé Google Maps. Nous avons scanné 30 998 applis vibe-codées et compté ce qui fuit vraiment.","2026-09-02","\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps\u002Fcard-800x500.png",{"path":1254,"title":1255,"description":1256,"published":1257,"category":907,"image":1258,"draft":804},"\u002Fblog\u002Fsupabase-backup-tools-compared","Outils de sauvegarde Supabase comparés, le nôtre compris","Quatre familles d'outil de sauvegarde Supabase, ce que chacun copie vraiment, et quand une GitHub Action gratuite bat le fait de payer, nous compris.","2026-09-01","\u002Fblog\u002Fsupabase-backup-tools-compared\u002Fcard-800x500.png",{"path":1260,"title":1261,"description":1262,"published":1263,"category":800,"image":1264,"draft":804},"\u002Fblog\u002Freplit-secrets-explained","Utiliser les secrets sur Replit, et ce qui est publié quand même","Utiliser les secrets sur Replit : en ajouter un, le relire, corriger les deux causes du undefined. Et les clés que l'outil Secrets ne peut pas protéger.","2026-08-31","\u002Fblog\u002Freplit-secrets-explained\u002Fcard-800x500.png",{"path":1266,"title":1267,"description":1268,"published":1269,"category":907,"image":1270,"draft":804},"\u002Fblog\u002Fback-up-supabase-free-tier","Sauvegardes du plan gratuit Supabase : en faire une sans terminal","Le plan gratuit de Supabase ne fait aucune sauvegarde : la copie vient de vous. Comment la faire depuis le tableau de bord, et ce que le CSV oublie.","2026-08-30","\u002Fblog\u002Fback-up-supabase-free-tier\u002Fcard-800x500.png",{"path":1272,"title":1273,"description":1274,"published":1275,"category":800,"image":1276,"draft":804},"\u002Fblog\u002Fis-supabase-secure","Supabase est-il sûr ? Oui. Votre projet est une autre question","Supabase est-il sûr ? La plateforme est auditée, chiffrée et testée en intrusion. Sa documentation dit où cela s'arrête et où vos réglages commencent.","2026-08-29","\u002Fblog\u002Fis-supabase-secure\u002Fcard-800x500.png",{"path":1278,"title":1279,"description":1280,"published":1281,"category":800,"image":1282,"draft":804},"\u002Fblog\u002Fwhere-to-find-supabase-api-keys","Où trouver vos clés API Supabase : anon, service_role et URL","L'URL du projet, la clé anon et la clé service_role tiennent sur une page du tableau de bord. Voici où elle est, et laquelle des quatre va dans votre app.","2026-08-28","\u002Fblog\u002Fwhere-to-find-supabase-api-keys\u002Fcard-800x500.png",{"path":554,"title":1284,"description":1285,"published":1286,"category":800,"image":1287,"draft":804},"New row violates row-level security policy sur Supabase : et après ?","\"New row violates row-level security policy\" signifie que Supabase a refusé une écriture. Le correctif rapide rouvre la table à tout le monde.","2026-08-27","\u002Fblog\u002Fnew-row-violates-row-level-security-policy\u002Fcard-800x500.png",{"path":1289,"title":1290,"description":1291,"published":1292,"category":800,"image":1293,"draft":804},"\u002Fblog\u002Fcors-wildcard-security-risk","Un joker CORS est-il un risque de sécurité ? Rarement.","Un joker CORS est-il un risque de sécurité ? C'est souvent le réglage par défaut de votre builder, et il ne livre rien que votre serveur ne donnait déjà.","2026-08-26","\u002Fblog\u002Fcors-wildcard-security-risk\u002Fcard-800x500.png",{"path":1295,"title":1296,"description":1297,"published":1298,"category":907,"image":1299,"draft":804},"\u002Fblog\u002Fsupabase-branching-is-not-a-backup","Le branching Supabase n'est pas une sauvegarde de vos données.","Pourquoi le branching Supabase n'est pas une sauvegarde : une branche démarre sans vos données, et un merge ne déplace que le schéma. À quoi il sert vraiment.","2026-08-25","\u002Fblog\u002Fsupabase-branching-is-not-a-backup\u002Fcard-800x500.png",{"path":1301,"title":1302,"description":1303,"published":1304,"category":907,"image":1305,"draft":804},"\u002Fblog\u002Fhow-to-restore-a-supabase-backup","Restaurer une sauvegarde Supabase, et ce qui casse après","Restaurer une sauvegarde Supabase depuis le tableau de bord ou un fichier de dump, ce que la restauration remplace et pourquoi votre app peut rester cassée.","2026-08-24","\u002Fblog\u002Fhow-to-restore-a-supabase-backup\u002Fcard-800x500.png",{"path":1307,"title":1308,"description":1309,"published":1310,"category":800,"image":1311,"draft":804},"\u002Fblog\u002Fsupabase-storage-bucket-public","Votre bucket Supabase Storage est public. Est-ce un problème ?","Un bucket Supabase Storage public signifie que celui qui a l'URL d'un fichier peut l'ouvrir. Il ne signifie pas que quelqu'un peut lister ce qu'il contient.","2026-08-23","\u002Fblog\u002Fsupabase-storage-bucket-public\u002Fcard-800x500.png",{"path":1313,"title":1314,"description":1315,"published":1316,"category":800,"image":1317,"draft":804},"\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend","Une clé API Google dans votre frontend, est-ce un problème ?","Une clé API Google exposée dans votre frontend est la clé que notre scanner trouve le plus souvent, et en général tout va bien. Un réglage gratuit tranche.","2026-08-22","\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":1319,"title":1320,"description":1321,"published":1322,"category":907,"image":1323,"draft":804},"\u002Fblog\u002Fai-agent-deleted-my-database","Un agent IA a supprimé mes données Supabase. Que puis-je récupérer ?","Un agent IA a supprimé des données de votre base. Ce que vous pouvez récupérer était déjà décidé avant, et les prochaines minutes décident du reste.","2026-08-21","\u002Fblog\u002Fai-agent-deleted-my-database\u002Fcard-800x500.png",{"path":1325,"title":1326,"description":1327,"published":1328,"category":800,"image":1329,"draft":804},"\u002Fblog\u002Fcan-anyone-read-your-supabase-database","Qui peut lire votre base Supabase ? Nous avons testé 3 680 apps","N'importe qui peut-il lire votre base de données Supabase sans se connecter ? Nous avons scanné 30 998 apps en ligne créées avec des builders IA.","2026-08-18","\u002Fblog\u002Fcan-anyone-read-your-supabase-database\u002Fcard-800x500.png",{"path":1331,"title":1332,"description":1333,"published":1328,"category":800,"image":1334,"draft":804},"\u002Fblog\u002Fsource-maps-exposed-in-production","Source maps exposées : votre app publie son code original","Une source map exposée laisse n'importe qui lire le code original de votre app, commentaires compris. Le test de 30 secondes, et ce qui compte vraiment.","\u002Fblog\u002Fsource-maps-exposed-in-production\u002Fcard-800x500.png",{"path":1336,"title":1337,"description":1338,"published":1339,"category":800,"image":1340,"draft":804},"\u002Fblog\u002Fsupabase-new-api-keys","Supabase publishable key : la même chose que l'anon key ?","Supabase a remplacé anon et service_role par des clés publiables et secrètes. Laquelle a sa place dans votre app, et laquelle jamais ?","2026-08-12","\u002Fblog\u002Fsupabase-new-api-keys\u002Fcard-800x500.png",{"path":1342,"title":1343,"description":1344,"published":1345,"category":907,"image":1346,"draft":804},"\u002Fblog\u002Fdoes-supabase-back-up-my-database","Supabase sauvegarde-t-il ma base de données ? Cela dépend du plan.","Supabase sauvegarde-t-il votre base de données ? Chaque jour sur les plans payants, jamais sur le gratuit. Comment savoir ce que vous avez vraiment.","2026-08-11","\u002Fblog\u002Fdoes-supabase-back-up-my-database\u002Fcard-800x500.png",{"path":1348,"title":1349,"description":1350,"published":1351,"category":800,"image":1352,"draft":804},"\u002Fblog\u002Fsupabase-rls-on-but-table-still-public","Supabase Row Level Security est activé. La table reste publique.","Activer Supabase Row Level Security ne protège pas une table. Ce sont vos politiques, et celle qui a réparé votre app laisse peut-être entrer tout le monde.","2026-08-10","\u002Fblog\u002Fsupabase-rls-on-but-table-still-public\u002Fcard-800x500.png",{"path":1354,"title":1355,"description":1356,"published":1351,"category":907,"image":1357,"draft":804},"\u002Fblog\u002Fversion-history-is-not-a-backup","L'historique des versions n'est pas une sauvegarde de vos données.","Lovable et Bolt tiennent un historique des versions de votre code. Votre base de données est un service à part, et revenir en arrière ne la ramène pas.","\u002Fblog\u002Fversion-history-is-not-a-backup\u002Fcard-800x500.png",{"path":1359,"title":1360,"description":1361,"published":1362,"category":907,"image":1363,"draft":804},"\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database","Trois façons de sauvegarder une base Supabase, et ce qui manque","Le tableau de bord, pg_dump et un service géré. Ce que chacun enregistre, ce qu'il laisse de côté sans le dire, et lequel survit à la perte du compte.","2026-08-09","\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database\u002Fcard-800x500.png",{"path":1365,"title":1366,"description":1367,"published":1362,"category":800,"image":1368,"draft":804},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","Quelles clés API sont sûres côté navigateur, et lesquelles non","Votre clé anon Supabase est faite pour être publique. Votre clé service_role non, et elle ignore toutes les règles que vous posez. Comment les distinguer.","\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend\u002Fcard-800x500.png",1794213641928]