[{"data":1,"prerenderedAt":1336},["ShallowReactive",2],{"blog-de-null-value-violates-not-null-constraint":3,"blog-index-de":810},{"id":4,"title":5,"body":6,"category":768,"cover":769,"coverAlt":770,"description":771,"draft":772,"extension":773,"faq":774,"image":790,"keywords":791,"meta":799,"navigation":800,"ogTitle":5,"path":801,"published":802,"seo":803,"stem":804,"tldr":805,"updated":802,"__hash__":809},"blog_de\u002Fblog\u002Fnull-value-violates-not-null-constraint.md","Supabase-Fehler \"null value in column violates not-null constraint\"",{"type":7,"value":8,"toc":756},"minimark",[9,13,24,35,38,43,46,49,171,180,184,187,201,216,241,258,293,308,312,321,350,367,375,381,387,394,400,404,407,420,426,429,435,443,448,452,455,463,474,484,490,496,514,545,549,552,563,570,573,597,615,618,624,627,633,640,645,649,652,661,683,692,706,710,749],[10,11,12],"p",{},"Du drückst in deiner App bei einer neuen Aufgabe auf Speichern, und nichts wird\ngespeichert. Die Seite zeigt keinen Fehler, und die Liste bleibt, wie sie war.\nDie Browserkonsole kennt den Grund:",[14,15,20],"pre",{"className":16,"code":18,"language":19},[17],"language-text","{\n  \"code\": \"23502\",\n  \"details\": null,\n  \"hint\": null,\n  \"message\": \"null value in column \\\"category_id\\\" of relation \\\"tasks\\\" violates not-null constraint\"\n}\n","text",[21,22,18],"code",{"__ignoreMap":23},"",[10,25,26,27,30,31,34],{},"Supabase \"null value in column violates not-null constraint\" heißt, dass eine\nZeile ohne Wert in einer Spalte bei deiner Datenbank angekommen ist, die immer\neinen Wert haben muss, und Postgres die ganze Zeile abgelehnt hat. Hier ist die\nSpalte ",[21,28,29],{},"category_id"," in deiner Tabelle ",[21,32,33],{},"tasks",". Beim Speichern wurde keine\nKategorie mitgeschickt.",[10,36,37],{},"Die schnellste Lösung ist, die Regel zu entfernen: eine Zeile SQL, und die Spalte\ndarf leer bleiben. Der Fehler verschwindet, und ab dann kommen Zeilen mit leerem\nFeld an, auf die der Rest deiner App an einer Stelle trifft, an der du nicht\nhingeschaut hast. Finde zuerst heraus, woher der leere Wert kam. Es ist fast immer\neine von wenigen Stellen.",[39,40,42],"h2",{"id":41},"was-bedeutet-null-value-in-column-violates-not-null-constraint","Was bedeutet \"null value in column violates not-null constraint\"?",[10,44,45],{},"Eine Spalte der Zeile, die deine App speichern wollte, war leer, diese Spalte darf\nnie leer sein, und Postgres hat sich geweigert, irgendetwas von der Zeile zu\nspeichern. Geschrieben wurde nichts. Null ist das Wort von Postgres für gar keinen\nWert, und das ist etwas anderes als eine Null als Zahl oder ein Text ohne\nBuchstaben.",[10,47,48],{},"Stell dir jede Zeile als Papierformular vor und die Tabelle als Aktenschublade, in\ndie es kommt. Eine Not-null-Regel ist ein Sternchen neben einem Feld. Postgres\nsitzt an der Schublade und legt kein Formular ab, bei dem ein Feld mit Sternchen\nleer ist. Das Formular kommt zurück, und die Meldung sagt, welches Feld.",[50,51,52,68],"table",{},[53,54,55],"thead",{},[56,57,58,62,65],"tr",{},[59,60,61],"th",{},"Feld",[59,63,64],{},"Beispiel",[59,66,67],{},"Was es dir sagt",[69,70,71,97,112,131,145],"tbody",{},[56,72,73,78,83],{},[74,75,76],"td",{},[21,77,21],{},[74,79,80],{},[21,81,82],{},"23502",[74,84,85,86,89,90],{},"Der Code von Postgres für diesen Fehler, ",[21,87,88],{},"not_null_violation"," in ",[91,92,96],"a",{"href":93,"rel":94},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Futils\u002Ferrcodes.txt",[95],"nofollow","seiner Liste der Codes",[56,98,99,104,109],{},[74,100,101],{},[21,102,103],{},"message",[74,105,106],{},[21,107,108],{},"null value in column \"category_id\" of relation \"tasks\" violates not-null constraint",[74,110,111],{},"Die leere Spalte, dann die Tabelle. \"Relation\" ist das Wort von Postgres für eine Tabelle",[56,113,114,119,128],{},[74,115,116],{},[21,117,118],{},"details",[74,120,121,124,125],{},[21,122,123],{},"Failing row contains (…)"," oder ",[21,126,127],{},"null",[74,129,130],{},"Eine Kopie der abgelehnten Zeile, wenn Postgres entscheidet, dass der Aufrufer sie sehen darf. Ein Abschnitt weiter unten zeigt, wann das so ist",[56,132,133,138,142],{},[74,134,135],{},[21,136,137],{},"hint",[74,139,140],{},[21,141,127],{},[74,143,144],{},"Nichts hinzuzufügen bei diesem Fehler",[56,146,147,150,155],{},[74,148,149],{},"HTTP-Status",[74,151,152],{},[21,153,154],{},"400 Bad Request",[74,156,157,158,161,162,164,165,170],{},"PostgREST, der Teil von Supabase, der Aufrufe ",[21,159,160],{},"supabase.from(...)"," in SQL übersetzt, hat für ",[21,163,82],{}," keinen eigenen Fall, also landet ",[91,166,169],{"href":167,"rel":168},"https:\u002F\u002Fgithub.com\u002FPostgREST\u002Fpostgrest\u002Fblob\u002Fmain\u002Fsrc\u002Flibrary\u002FPostgREST\u002FError.hs",[95],"sein Code"," bei 400",[10,172,173,174,179],{},"Der Wortlaut stammt von Postgres selbst, aus\n",[91,175,178],{"href":176,"rel":177},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Fexecutor\u002FexecMain.c",[95],"der Funktion, die den Fehler auslöst",",\nund er liest sich gleich, ob die Zeile aus deiner App, einem Trigger oder dem SQL\nEditor kam.",[39,181,183],{"id":182},"woher-kam-der-leere-wert","Woher kam der leere Wert?",[10,185,186],{},"Meist von einer von drei Stellen im Code deiner eigenen App, und die Spalte in der\nMeldung sagt dir, wo du zuerst suchst. Zwei weitere beginnen in der Datenbank.",[10,188,189,193,194,196,197,200],{},[190,191,192],"strong",{},"Ein Feld, das deine App nie schickt."," Das ist der häufigste Fall. Die Spalte\nwurde zur Pflicht, nachdem das Formular gebaut war: Ein KI-Builder hat\n",[21,195,29],{}," zur Tabelle hinzugefügt, als du nach Kategorien gefragt hast, sie\nals Pflicht markiert und das Formular, das Aufgaben speichert, so gelassen, wie es\nwar. Nichts in der Anfrage nennt die Spalte, die Spalte hat keinen Standardwert,\nalso ist sie leer. Vergleich die Spalten in deinem Table Editor mit dem Objekt, das\ndeine App an ",[21,198,199],{},".insert()"," übergibt. Die fehlende ist die aus der Meldung.",[10,202,203,204,207,208,210,211,215],{},"Ein Schlüssel, der anders geschrieben ist als seine Spalte, kommt gar nicht so\nweit. PostgREST lehnt ",[21,205,206],{},"categoryId"," für eine Spalte namens ",[21,209,29],{}," ab, bevor\nPostgres die Zeile sieht, und\n",[91,212,214],{"href":213},"\u002Fblog\u002Fsupabase-schema-cache-column-error","dieser Fehler hat einen eigenen Beitrag",".",[10,217,218,221,222,225,226,229,230,237,238,240],{},[190,219,220],{},"Eine Null, die dein Code absichtlich schickt."," ",[21,223,224],{},"{ category_id: selected?.id ?? null }","\nschickt eine ausdrückliche Null, wenn nichts ausgewählt ist, und eine ausdrückliche\nNull ist für die Anfrage ein Wert. JavaScripts ",[21,227,228],{},"undefined"," verhält sich anders.\nJSON kennt diesen Wert nicht, also wird er von\n",[91,231,234],{"href":232,"rel":233},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FJavaScript\u002FReference\u002FGlobal_Objects\u002FJSON\u002Fstringify",[95],[21,235,236],{},"JSON.stringify","\n\"omitted (when found in an object)\", und ein Schlüssel mit ",[21,239,228],{}," erreicht\nSupabase überhaupt nicht. Welches von beiden dein Code erzeugt, entscheidet, ob ein\nStandardwert einspringen kann, und darum geht es im nächsten Abschnitt.",[10,242,243,221,246,249,250,253,254,257],{},[190,244,245],{},"Eine Spalte ohne den Standardwert, den du angenommen hast.",[21,247,248],{},"created_at"," füllt\nsich meist von selbst, weil die Tabelle ",[21,251,252],{},"default now()"," sagt. ",[21,255,256],{},"user_id"," meist\nnicht, denn nichts in Postgres weiß, wer angemeldet ist, solange man der Spalte\nnicht sagt, wie sie es herausfindet. Ein späterer Abschnitt zeigt, wie.",[10,259,260,263,264,267,268,271,272,277,278,281,282,284,285,289,290,292],{},[190,261,262],{},"Eine Registrierung, unter einer anderen Meldung."," Ein Trigger, der jeden neuen\nNutzer in eine Tabelle ",[21,265,266],{},"profiles"," kopiert, liest seine Registrierungsdaten mit\n",[21,269,270],{},"->>",", und\n",[91,273,276],{"href":274,"rel":275},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Ffunctions-json.html",[95],"die JSON-Operatoren von Postgres","\n\"return NULL, rather than failing, if the JSON input does not have the right\nstructure to match the request; for example if no such key or array element\nexists\". Ein Registrierungsformular, das nie ",[21,279,280],{},"full_name"," geschickt hat, gibt dem\nTrigger einen leeren Namen, eine Pflichtspalte ",[21,283,280],{}," lehnt ihn ab, und\nSupabase meldet die ganze Registrierung als\n",[91,286,288],{"href":287},"\u002Fblog\u002Fdatabase-error-saving-new-user","\"Database error saving new user\"",". Den\n",[21,291,82],{}," selbst findest du in den Postgres-Logs, und dieser Beitrag zeigt, wo.",[10,294,295,298,299,302,303,307],{},[190,296,297],{},"Ein Löschen an anderer Stelle."," Ein Fremdschlüssel mit ",[21,300,301],{},"on delete set null","\nleert die Spalte in jeder Zeile, die auf die gelöschte zeigte. Ist diese Spalte\nzugleich Pflicht, scheitert stattdessen das Löschen mit dieser Meldung, und\n",[91,304,306],{"href":305},"\u002Fblog\u002Fdatabase-error-deleting-user","der Beitrag über das Löschen von Nutzern","\nzeigt, wie du eine passende Regel wählst.",[39,309,311],{"id":310},"aber-die-spalte-hat-doch-einen-standardwert","\"Aber die Spalte hat doch einen Standardwert\"",[10,313,314,315,320],{},"Dann hat die Anfrage die Spalte genannt und leer geschickt. Postgres setzt einen\nStandardwert nur ein, wenn eine Spalte ganz aus der Zeile weggelassen wird.\n",[91,316,319],{"href":317,"rel":318},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fddl-default.html",[95],"Seine Dokumentation zu Standardwerten","\nsagt: \"When a new row is created and no values are specified for some of the\ncolumns, those columns will be filled with their respective default values.\"\nEine ausdrückliche Null zählt als angegebener Wert. Auf dem Formular gilt ein Feld\nmit vorgedrucktem Wert als ausgefüllt, bis jemand \"keins\" über den Aufdruck\nschreibt.",[10,322,323,324,329,330,333,334,336,337,340,341,346,347,349],{},"supabase-js schreibt \"keins\" an einer Stelle darüber, an der du es nicht erwarten\nwürdest: bei einem Mehrfach-Insert, bei dem du ein Array von Zeilen übergibst. Es\nnennt jede Spalte, die in irgendeiner der Zeilen vorkommt, und\n",[91,325,328],{"href":326,"rel":327},"https:\u002F\u002Fgithub.com\u002Fsupabase\u002Fsupabase-js\u002Fblob\u002Fmaster\u002Fpackages\u002Fcore\u002Fpostgrest-js\u002Fsrc\u002FPostgrestQueryBuilder.ts",[95],"sein Quellcode","\nbeschreibt die Option, die über die Lücken entscheidet, ",[21,331,332],{},"defaultToNull",": \"Make\nmissing fields default to ",[21,335,127],{},". Otherwise, use the default value for the column.\nOnly applies for bulk inserts.\" Sie steht auf ",[21,338,339],{},"true",", solange du sie nicht änderst.\n",[91,342,345],{"href":343,"rel":344},"https:\u002F\u002Fdocs.postgrest.org\u002Fen\u002Fstable\u002Freferences\u002Fapi\u002Fpreferences.html",[95],"Die Dokumentation von PostgREST","\nsagt dasselbe für eine Anfrage, die ihre Spalten aufzählt, und genau das schickt\nsupabase-js bei einem Array: \"any missing columns in the payload will be inserted\nas ",[21,348,127],{}," value by default.\"",[10,351,352,353,356,357,359,360,363,364,366],{},"Hier geht also die zweite Aufgabe mit leerem ",[21,354,355],{},"status"," hinein, selbst wenn die\nTabelle für ",[21,358,355],{}," den Standardwert ",[21,361,362],{},"'todo'"," hat, und eine Pflichtspalte\n",[21,365,355],{}," lehnt das ganze Insert ab:",[14,368,373],{"className":369,"code":371,"language":372,"meta":23},[370],"language-js","await supabase.from('tasks').insert([\n  { title: 'Konzept schreiben', status: 'doing' },\n  { title: 'Rechnung schicken' },\n])\n","js",[21,374,371],{"__ignoreMap":23},[10,376,377,378,380],{},"Beide Reparaturen funktionieren. Schick ",[21,379,355],{}," in jeder Zeile mit, oder sag\nsupabase-js, dass die Standardwerte der Tabelle stehen bleiben sollen:",[14,382,385],{"className":383,"code":384,"language":372,"meta":23},[370],"await supabase.from('tasks').insert(rows, { defaultToNull: false })\n",[21,386,384],{"__ignoreMap":23},[10,388,389,390,393],{},"Diese Option fügt den Header ",[21,391,392],{},"Prefer: missing=default"," hinzu, den PostgREST liest.\nEine einzelne Zeile, als Objekt ohne Array darum übergeben, tut das nicht: Ein\nSchlüssel, der darin fehlt, fehlt auch im Insert, und der Standardwert greift.",[395,396],"diagram",{"alt":397,"caption":398,"src":399},"Dasselbe Insert mit zwei Zeilen, zweimal gezeichnet. Das zweite Formular lässt sein Sternchenfeld status weg. Links, unter defaultToNull: true, steht in diesem Feld null in Bernstein über dem vorgedruckten Standardwert, und das Insert endet bei 23502. Rechts, unter defaultToNull: false, ist der vorgedruckte Standardwert zu sehen, und das Insert bekommt einen Haken.","Bei einem Mehrfach-Insert geht eine Spalte, die eine Zeile hat und eine andere nicht, bei der zweiten als null hinein. defaultToNull: false lässt stattdessen den Standardwert der Tabelle einspringen.","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fnull-over-the-default-1600x720.png",[39,401,403],{"id":402},"warum-scheitert-ein-upsert-daran-obwohl-die-zeile-existiert","Warum scheitert ein Upsert daran, obwohl die Zeile existiert?",[10,405,406],{},"Weil Postgres die neue Zeile auf leere Pflichtspalten prüft, bevor es nach der\nvorhandenen Zeile sucht, die es ändern würde. Ein Upsert ist ein Formular mit dem\nVermerk \"ablegen, oder falls es für diese Person schon eine Akte gibt, ändern\".\nPostgres prüft zuerst die Sternchen. Ein Formular, das nur ein neues Profilbild\nträgt, kommt zurück, obwohl die Akte, die es ändern sollte, in der Schublade liegt.",[10,408,409,410,415,416,419],{},"Die Reihenfolge steht in\n",[91,411,414],{"href":412,"rel":413},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Fexecutor\u002FnodeModifyTable.c",[95],"dem Code, der ein Insert ausführt",":\n\"Check the constraints of the tuple.\" kommt vor \"Perform a speculative\ninsertion.\", und das ist der Schritt, der die Kollision findet, auf die eine\nKlausel ",[21,417,418],{},"on conflict"," reagiert. Tuple ist das Wort von Postgres für eine Zeile.\nDieser Upsert, der eine Spalte eines vorhandenen Profils ändern soll, scheitert\nalso an jeder anderen Spalte, die die Tabelle verlangt:",[14,421,424],{"className":422,"code":423,"language":372,"meta":23},[370],"await supabase.from('profiles').upsert({ id: user.id, avatar_url: url })\n",[21,425,423],{"__ignoreMap":23},[10,427,428],{},"Um einige Spalten einer Zeile zu ändern, die schon da ist, nimm ein Update. Es\nlandet auf der gespeicherten Zeile, in der jede Pflichtspalte gefüllt ist, und\nändert nur, was du schickst:",[14,430,433],{"className":431,"code":432,"language":372,"meta":23},[370],"await supabase.from('profiles').update({ avatar_url: url }).eq('id', user.id)\n",[21,434,432],{"__ignoreMap":23},[10,436,437,438,442],{},"Heb dir den Upsert für Zeilen auf, die es vielleicht noch nicht gibt, und schick\njede Pflichtspalte mit.\n",[91,439,441],{"href":440},"\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint","Der Beitrag über doppelte Schlüssel","\nzeigt, was ein Upsert sonst noch braucht: auf welcher Spalte er abgleicht und\nwelche Leseregeln er aus dem Browser anspricht.",[395,444],{"alt":445,"caption":446,"src":447},"Zwei Bahnen, die mit derselben Änderung an einem Feld beginnen. In der Upsert-Bahn wird daraus eine neue Zeile, deren zweites Sternchenfeld leer ist, ein Tor mit Sternchen schließt sich in Bernstein mit 23502 darunter, und das Tor on conflict und die Schublade dahinter werden nie erreicht. In der Update-Bahn landet sie auf der gespeicherten Zeile, in der jedes Sternchenfeld gefüllt ist, das Tor ist offen, und die Schublade nimmt sie.","Die Zeile eines Upserts wird auf leere Pflichtspalten geprüft, bevor Postgres nach der Zeile sucht, die es ändern würde. Ein Update beginnt bei der gespeicherten Zeile, die sie schon hat.","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fupsert-is-checked-first-1600x780.png",[39,449,451],{"id":450},"solltest-du-die-not-null-regel-entfernen","Solltest du die NOT-NULL-Regel entfernen?",[10,453,454],{},"Nur wenn die Spalte nie Pflicht sein sollte. Eine Telefonnummer, die dein Formular\nals optional behandelt, ist so ein Fall, und dort ist das Entfernen der Regel die\nrichtige Reparatur:",[14,456,461],{"className":457,"code":459,"language":460,"meta":23},[458],"language-sql","alter table public.profiles alter column phone drop not null;\n","sql",[21,462,459],{"__ignoreMap":23},[10,464,465,466,469,470,473],{},"Bei jeder anderen Spalte behalte die Regel und füll das Feld. Ohne sie kann jeder\nZeile, die danach gespeichert wird, der Wert fehlen, den die Regel garantiert hat,\nund deine App trifft woanders auf die Lücke: Eine Seite, die ",[21,467,468],{},"task.category.name","\nliest, bricht mit ",[21,471,472],{},"Cannot read properties of null"," ab, oder eine Zählung der\nAufgaben pro Kategorie verliert stillschweigend die ohne Kategorie.",[10,475,476,477,479,480,483],{},"Die Spalte, bei der das Entfernen am meisten kostet, ist ",[21,478,256],{},". Eine Zeile mit\nleerem Eigentümer sieht niemand. Eine Leseregel wie ",[21,481,482],{},"auth.uid() = user_id"," trifft\nnie auf sie zu, weil in SQL ein leerer Wert gleich nichts ist, nicht einmal einem\nanderen leeren Wert. Die Aufgabe ist gespeichert, und von da an sieht sie niemand,\nauch nicht die Person, die sie angelegt hat.",[10,485,486,487,489],{},"Die Reparatur für ",[21,488,256],{}," ist ein Standardwert, der einsetzt, wer gerade\nangemeldet ist:",[14,491,494],{"className":492,"code":493,"language":460,"meta":23},[458],"alter table public.tasks\n  alter column user_id set default auth.uid();\n",[21,495,493],{"__ignoreMap":23},[10,497,498,499,503,504,507,508,513],{},"Postgres wertet einen Standardwert \"whenever the default value is inserted (not\nwhen the table is created)\" aus, mit\n",[91,500,502],{"href":317,"rel":501},[95],"seinen eigenen Worten",",\nalso bekommt jede neue Zeile die id der Person, die speichert. Ist niemand\nangemeldet, gibt ",[21,505,506],{},"auth.uid()"," null zurück, wie\n",[91,509,512],{"href":510,"rel":511},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Fdatabase\u002Fpostgres\u002Frow-level-security",[95],"Supabases Leitfaden zu Row Level Security","\nwarnt, und das Speichern scheitert weiterhin. So tut die Regel ihre Arbeit. Der\nStandardwert füllt das Feld nur, wenn deine App es weglässt, also hält weiterhin\ndeine Insert-Regel jemanden davon ab, die id einer anderen Person zu schicken.",[10,515,516,517,521,522,524,525,527,528,530,531,535,536,538,539,541,542,544],{},"Diese Insert-Regel ändert auch, welche Meldung du siehst. Postgres führt die\nRow-Level-Security-Prüfungen einer Tabelle an einer neuen Zeile vor ihren\nNot-null-Prüfungen aus:\n",[91,518,520],{"href":412,"rel":519},[95],"derselbe Insert-Code","\nhat \"Check any RLS WITH CHECK policies.\" vor \"Check the constraints of the\ntuple.\". Vergleicht deine Insert-Regel ",[21,523,256],{}," mit ",[21,526,506],{},", scheitert ein\nleeres ",[21,529,256],{}," also zuerst an der Regel und kommt als\n",[91,532,534],{"href":533},"\u002Fblog\u002Fnew-row-violates-row-level-security-policy","new row violates row-level security policy","\nzurück. Ein ",[21,537,82],{}," bei ",[21,540,256],{}," aus deiner App heißt eins von drei Dingen: Die\nInsert-Regel schaut sich ",[21,543,256],{}," nie an, die Regeln sind auf dieser Tabelle aus,\noder die Anfrage kam von Servercode, der sie überspringt.",[39,546,548],{"id":547},"ist-es-sicher-diesen-fehler-deinen-nutzern-zu-zeigen","Ist es sicher, diesen Fehler deinen Nutzern zu zeigen?",[10,550,551],{},"Nein. Zeig einen eigenen Satz und behalte den rohen Fehler in deinen Logs, denn\ndie Detailzeile ist, wenn Postgres sie mitschickt, eine Kopie der ganzen Zeile.",[10,553,554,555,559,560,562],{},"Die Detailzeile eines doppelten Schlüssels wiederholt einen Wert. Diese hier\nwiederholt das Formular.\n",[91,556,558],{"href":176,"rel":557},[95],"Die Funktion, die sie schreibt",",\ngibt die Zeile als ",[21,561,123],{}," aus, mit jeder Spalte, die der\nAufrufer lesen darf. Bei einem Update ist das die Zeile, wie sie gespeichert worden\nwäre, samt der Spalten, die niemand angefasst hat. Ein Formular, das Postgres\nzurückschickt, kann eine Fotokopie seiner selbst an der Ablehnung tragen, und auf\nder Kopie steht jedes Feld.",[10,564,565,566,569],{},"Postgres entscheidet, wer die Fotokopie bekommt, und sein Code prüft zuerst: \"Check\nif RLS is enabled and should be active for the relation; if so, then don't return\nanything.\" Eine Anfrage mit deinem öffentlichen Schlüssel an eine Tabelle mit\neingeschalteter Row Level Security bekommt ",[21,567,568],{},"\"details\": null",", und genau das hat die\nKonsole oben auf dieser Seite gezeigt.",[10,571,572],{},"Zwei Aufrufer bekommen die volle Kopie.",[10,574,575,578,579,582,583,586,587,591,592,596],{},[190,576,577],{},"Ein Browser, wenn die Regeln der Tabelle aus sind."," Zeigt die Konsole\n",[21,580,581],{},"Failing row contains"," für eine Anfrage, die deine App mit dem öffentlichen\nSchlüssel gestellt hat, ist Row Level Security auf dieser Tabelle aus. So liest\nauch ",[91,584,585],{"href":440},"der Beitrag über doppelte Schlüssel","\nseine eigene Detailzeile, und\n",[91,588,590],{"href":589},"\u002Fblog\u002Fenable-rls-on-every-supabase-table","die Regeln für jede Tabelle einschalten","\nzeigt den Schalter und die erste Regel. Eine Tabelle mit ausgeschalteten Regeln\ngibt einem Fremden ihre Zeilen genauso bereitwillig wie dir. Unser\n",[91,593,595],{"href":594},"\u002Fsecurity-scanner","kostenloser Scan"," liest deine Live-App mit dem öffentlichen\nSchlüssel, den jeder Besucher bekommt, und listet die Tabellen auf, die Zeilen\nherausgeben. Er braucht kein Konto.",[10,598,599,602,603,606,607,610,611,614],{},[190,600,601],{},"Servercode mit dem geheimen Schlüssel."," Supabases Leitfaden sagt, der geheime\nSchlüssel \"authorizes access through the ",[21,604,605],{},"service_role"," Postgres role, which has\nthe ",[21,608,609],{},"bypassrls"," attribute\", also greifen die Regeln nie, und die Detailzeile ist\nimmer gefüllt. Eine Edge Function, die eine Zeile ändert und mit dem Fehler\nantwortet, den sie bekommen hat, ",[21,612,613],{},"return new Response(JSON.stringify(error))",",\nschickt dem Browser jede Spalte der Zeile, die sie ändern wollte, egal wem diese\nZeile gehört.",[10,616,617],{},"Behalte den Fehler im Servercode und antworte mit eigenen Worten:",[14,619,622],{"className":620,"code":621,"language":372,"meta":23},[370],"if (error) {\n  console.error(error)\n  return new Response(JSON.stringify({ message: 'Das konnte nicht gespeichert werden.' }), {\n    status: 400,\n  })\n}\n",[21,623,621],{"__ignoreMap":23},[10,625,626],{},"Fang in der App den Code ab und sag, was die Person tun kann:",[14,628,631],{"className":629,"code":630,"language":372,"meta":23},[370],"const { error } = await supabase.from('tasks').insert(task)\n\nif (error?.code === '23502') {\n  setMessage('Wähl vor dem Speichern eine Kategorie.')\n} else if (error) {\n  setMessage('Etwas ist schiefgegangen. Bitte versuch es noch einmal.')\n}\n",[21,632,630],{"__ignoreMap":23},[10,634,635,639],{},[91,636,638],{"href":637},"\u002Fblog\u002Fmove-a-secret-to-an-edge-function","Einen geheimen Schlüssel in eine Edge Function verlegen","\nzeigt die Seite der Funktion, falls der geheime Schlüssel noch in deiner App\nsteckt.",[395,641],{"alt":642,"caption":643,"src":644},"Dieselbe abgelehnte Zeile, zweimal in dieselbe Schublade geschickt. Links, aus einem Browserfenster, trägt die Schublade ein Vorhängeschloss, und die Antwort lautet code 23502, 400 und details null. Rechts, aus einer Funktion mit einem Schlüssel, hat die Schublade kein Schloss, und die Zeile details der Antwort, Failing row contains, ist bernsteinfarben, mit einer Reihe bernsteinfarbener Balken darunter für jede Spalte.","Mit eingeschalteter Row Level Security bekommt der Browser keine Kopie der Zeile. Servercode mit dem geheimen Schlüssel bekommt sie ganz und reicht sie weiter, wenn er den Fehler so zurückgibt, wie er kam.","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fthe-whole-row-comes-back-1600x760.png",[39,646,648],{"id":647},"bevor-du-die-regeln-einer-spalte-änderst","Bevor du die Regeln einer Spalte änderst",[10,650,651],{},"Behalte eine Kopie der Datenbank von vor dem Moment, in dem du einen Standardwert\nhinzufügst, eine Regel entfernst oder einen Wert über alle Zeilen hinweg einträgst,\nund erfahre es, wenn eine Tabelle anfängt, Fremden zu antworten.",[10,653,654],{},[190,655,656,660],{},[91,657,659],{"href":658},"\u002Fsupabase-backups","Reeve Care"," bewahrt eine Kopie deiner Supabase-Datenbank auf:",[662,663,664,668,671,674,677,680],"ul",{},[665,666,667],"li",{},"eine verschlüsselte Kopie nach Zeitplan, auf Reeves eigenem Speicher in der EU, außerhalb deines Supabase-Kontos",[665,669,670],{},"jede Zeile, wie sie war, bevor ein Update über die ganze Tabelle sie umgeschrieben hat",[665,672,673],{},"jede Kopie wird geprüft, bevor sie zählt, indem die Zeilen in jeder Tabelle gezählt werden",[665,675,676],{},"auch deine hochgeladenen Dateien, sobald du eine Storage-Zugangsberechtigung verbindest",[665,678,679],{},"Wiederherstellung mit einem Klick, vorher wird eine Sicherheitskopie des aktuellen Stands angelegt",[665,681,682],{},"alles, was Monitor macht",[10,684,685],{},[190,686,687,691],{},[91,688,690],{"href":689},"\u002Fwebsite-monitoring","Reeve Monitor"," prüft weiter von außen:",[662,693,694,697,700,703],{},[665,695,696],{},"ob deine App erreichbar ist, jede Minute geprüft",[665,698,699],{},"der vollständige Sicherheitscheck jede Stunde, für bis zu drei Apps",[665,701,702],{},"eine E-Mail an dem Tag, an dem ein erneuter Scan deine App schlechter benotet als der vorige, sodass eine Tabelle, deren Regeln abgeschaltet wurden, dich erreicht, ohne dass du nachsehen musst",[665,704,705],{},"ein monatlicher Bericht in klarer Sprache über das, was er gesehen hat",[39,707,709],{"id":708},"was-du-jetzt-tun-solltest","Was du jetzt tun solltest",[711,712,713],"key-takeaways",{},[662,714,715,718,724,731,738,746],{},[665,716,717],{},"Lies Spalte und Tabelle in der Meldung. Das ist das Feld, das leer ankam.",[665,719,720,721,723],{},"Prüf, ob deine App dieses Feld weglässt, es als ",[21,722,127],{}," schickt oder einen Standardwert erwartet, den die Spalte nicht hat.",[665,725,726,727,730],{},"Um einige Spalten einer vorhandenen Zeile zu ändern, nimm ",[21,728,729],{},".update()"," mit einem Filter. Ein Upsert muss jede Pflichtspalte mitbringen.",[665,732,733,734,737],{},"Schick bei einem Mehrfach-Insert jede Spalte in jeder Zeile mit, oder übergib ",[21,735,736],{},"{ defaultToNull: false }",", damit die Standardwerte der Tabelle stehen bleiben.",[665,739,740,741,743,744,215],{},"Behalte die Regel auf ",[21,742,256],{}," und gib der Spalte den Standardwert ",[21,745,506],{},[665,747,748],{},"Schreib den rohen Fehler im Servercode ins Log und antworte dem Browser mit einem eigenen Satz.",[10,750,751,752,755],{},"Fang bei der Spalte in der Meldung an, dann bei der Zeile deines Codes, die die\nZeile zusammenbaut. Ändert die Reparatur die Tabelle selbst, mit einem neuen\nStandardwert oder einem Update über alle Zeilen,\n",[91,753,754],{"href":658},"behalte eine Kopie der Datenbank"," von vor dem Ausführen.",{"title":23,"searchDepth":757,"depth":757,"links":758},3,[759,761,762,763,764,765,766,767],{"id":41,"depth":760,"text":42},2,{"id":182,"depth":760,"text":183},{"id":310,"depth":760,"text":311},{"id":402,"depth":760,"text":403},{"id":450,"depth":760,"text":451},{"id":547,"depth":760,"text":548},{"id":647,"depth":760,"text":648},{"id":708,"depth":760,"text":709},"Sicherheitsgrundlagen","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fcover-1200x630.png","Ein Papierformular vor einer offenen Aktenschublade, sein zweites Sternchenfeld leer und bernsteinfarben umrandet.","Supabase \"null value in column violates not-null constraint\": Eine Pflichtspalte kam leer an. Woher die Null kommt, und die Lösung.",false,"md",[775,778,781,784,787],{"q":776,"a":777},"Was bedeutet null value in column violates not-null constraint?","Deine App wollte eine Zeile ohne Wert in einer Spalte speichern, die immer einen haben muss, und Postgres hat die ganze Zeile abgelehnt. Geschrieben wurde nichts. Die Meldung nennt Spalte und Tabelle. Der Code ist 23502, und aus einer Supabase-App kommt er als HTTP 400.",{"q":779,"a":780},"Warum ist die Spalte null, obwohl sie einen Standardwert hat?","Weil die Anfrage die Spalte genannt und leer geschickt hat. Postgres setzt einen Standardwert nur ein, wenn die Spalte weggelassen wird. supabase-js tut genau das bei einem Mehrfach-Insert: Eine Spalte, die eine Zeile hat und eine andere nicht, geht bei der zweiten als null hinein, außer du übergibst defaultToNull: false.",{"q":782,"a":783},"Warum scheitert mein Upsert mit einem Not-null-Fehler, obwohl die Zeile existiert?","Postgres prüft die neue Zeile auf leere Pflichtspalten, bevor es nach der vorhandenen Zeile sucht, die es ändern würde. Ein Upsert, der nur die zu ändernden Spalten trägt, scheitert an jeder anderen Pflichtspalte. Für eine Zeile, die es schon gibt, nimm update mit einem Filter.",{"q":785,"a":786},"Soll ich die NOT-NULL-Regel entfernen?","Nur wenn die Spalte nie Pflicht sein sollte, etwa eine Telefonnummer, die dein Formular als optional behandelt. Sonst verschwindet der Fehler, und Zeilen kommen ohne den Wert an, auf den deine App dann woanders trifft. Eine Zeile mit leerem user_id sieht niemand. Finde heraus, woher die Null kommt, oder gib der Spalte einen Standardwert wie auth.uid().",{"q":788,"a":789},"Ist es sicher, diesen Fehler Nutzern zu zeigen?","Nein. Wenn Postgres die Detailzeile mitschickt, ist sie eine Kopie der ganzen Zeile. Eine Browseranfrage an eine Tabelle mit eingeschalteter Row Level Security bekommt keine, Servercode mit dem geheimen Schlüssel dagegen immer. Fang den Code 23502 ab, zeig einen Satz, den du geschrieben hast, und behalte den rohen Fehler in deinen Logs.","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fcard-800x500.png",[792,793,794,795,796,797,798],"null value in column violates not-null constraint","supabase not null fehler","postgres 23502","failing row contains","supabase upsert not null","null value in column user_id violates not-null constraint","supabase defaulttonull",{},true,"\u002Fblog\u002Fnull-value-violates-not-null-constraint","2026-11-06",{"title":5,"description":771},"blog\u002Fnull-value-violates-not-null-constraint",[806,807,808],"Supabase \"null value in column violates not-null constraint\" (Postgres-Code 23502, HTTP 400 in deiner App) heißt: Eine Zeile kam ohne Wert in einer Spalte an, die immer einen haben muss. Die Meldung nennt Spalte und Tabelle. Gespeichert wurde nichts.","Der leere Wert kommt aus einem Feld, das deine App nie schickt, aus einer Null, die dein Code absichtlich schickt, oder aus einer Spalte ohne den Standardwert, den du angenommen hast. Ein Upsert und ein Mehrfach-Insert können ihn beide bei einer Zeile auslösen, die vollständig aussieht.","Behalte die Regel und repariere die Zeile. Gib aus Servercode nie den rohen Fehler an den Browser weiter: Seine Zeile \"Failing row contains\" kann die ganze Zeile ausgeben.","TE_wAwzonTizW0TvrAhuXy6J5wVnTijxECcHT1W0LcM",[811,817,823,829,830,836,842,848,854,860,866,872,878,884,890,896,902,907,913,919,924,930,936,942,948,954,960,965,971,977,983,989,995,1001,1007,1013,1018,1024,1030,1036,1042,1048,1054,1060,1066,1072,1077,1083,1089,1095,1101,1107,1113,1119,1125,1131,1137,1143,1149,1155,1161,1167,1172,1178,1184,1190,1196,1202,1208,1214,1220,1226,1232,1238,1244,1250,1255,1261,1267,1273,1279,1285,1291,1297,1302,1308,1314,1320,1325,1331],{"path":812,"title":813,"description":814,"published":815,"category":768,"image":816,"draft":772},"\u002Fblog\u002Fmax-client-connections-reached","Supabase \"max client connections reached\" erklärt","Supabase \"max client connections reached\": Der Pooler hat keinen Platz für noch einen Client. Der Port in deinem Connection String setzt das Limit.","2026-11-09","\u002Fblog\u002Fmax-client-connections-reached\u002Fcard-800x500.png",{"path":818,"title":819,"description":820,"published":821,"category":768,"image":822,"draft":772},"\u002Fblog\u002Funsupported-provider-provider-is-not-enabled","Supabase-Fehler: \"Unsupported provider: provider is not enabled\"","\"Unsupported provider: provider is not enabled\" heißt: Google-Login ist in deinem Supabase-Projekt aus. Die Lösung, und was bei redirect_uri_mismatch hilft.","2026-11-08","\u002Fblog\u002Funsupported-provider-provider-is-not-enabled\u002Fcard-800x500.png",{"path":824,"title":825,"description":826,"published":827,"category":768,"image":828,"draft":772},"\u002Fblog\u002Fviolates-foreign-key-constraint","Supabase-Fehler \"violates foreign key constraint\" beim Einfügen","Supabase \"insert or update on table violates foreign key constraint\": Deine Zeile zeigt auf eine, die es nicht gibt. Welche, warum, und die Lösung.","2026-11-07","\u002Fblog\u002Fviolates-foreign-key-constraint\u002Fcard-800x500.png",{"path":801,"title":5,"description":771,"published":802,"category":768,"image":790,"draft":772},{"path":831,"title":832,"description":833,"published":834,"category":768,"image":835,"draft":772},"\u002Fblog\u002Fauth-session-missing","Supabase \"Auth session missing!\": wo die Sitzung geblieben ist","\"Auth session missing!\" heißt: supabase-js fand keine angemeldete Sitzung und fragte Supabase gar nicht erst. Wo die Sitzung blieb, und die Lösung je Fall.","2026-11-05","\u002Fblog\u002Fauth-session-missing\u002Fcard-800x500.png",{"path":837,"title":838,"description":839,"published":840,"category":768,"image":841,"draft":772},"\u002Fblog\u002Finvalid-claim-missing-sub-claim","Supabase \"invalid claim: missing sub claim\": niemand ist angemeldet","\"invalid claim: missing sub claim\" heißt: Supabase Auth bekam ein echtes Token ohne Nutzer darin, deinen Projektschlüssel statt einer Sitzung.","2026-11-04","\u002Fblog\u002Finvalid-claim-missing-sub-claim\u002Fcard-800x500.png",{"path":843,"title":844,"description":845,"published":846,"category":768,"image":847,"draft":772},"\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function","\"Failed to send a request to the Edge Function\": der echte Grund","\"Failed to send a request to the Edge Function\" heißt: Der Browser hat keine lesbare Antwort bekommen. Seine Konsole sagt, warum, und ob deine Funktion lief.","2026-11-03","\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function\u002Fcard-800x500.png",{"path":849,"title":850,"description":851,"published":852,"category":768,"image":853,"draft":772},"\u002Fblog\u002Fsupabase-auth-redirects-to-localhost","Warum Supabase-E-Mail-Links auf localhost:3000 weiterleiten","Leitet Supabase auf localhost:3000 um, ist die Site URL unverändert, oder deine Adresse fehlt in Redirect URLs. Die Lösung und der Platzhalter, den du meidest.","2026-11-02","\u002Fblog\u002Fsupabase-auth-redirects-to-localhost\u002Fcard-800x500.png",{"path":855,"title":856,"description":857,"published":858,"category":768,"image":859,"draft":772},"\u002Fblog\u002Femail-link-is-invalid-or-has-expired","Supabase \"Email link is invalid or has expired\" bei einem neuen Link","Bei einem Link von vor einer Minute heißt Supabases \"Email link is invalid or has expired\" meist: Ein Mailfilter hat ihn zuerst benutzt. Prüfen und beheben.","2026-11-01","\u002Fblog\u002Femail-link-is-invalid-or-has-expired\u002Fcard-800x500.png",{"path":861,"title":862,"description":863,"published":864,"category":768,"image":865,"draft":772},"\u002Fblog\u002Fcanceling-statement-due-to-statement-timeout","Supabase \"canceling statement due to statement timeout\" erklärt","Supabase \"canceling statement due to statement timeout\": Eine Abfrage lief länger als ihre Rolle darf, 3 Sekunden für Besucher. Prüf zuerst die RLS-Regel.","2026-10-31","\u002Fblog\u002Fcanceling-statement-due-to-statement-timeout\u002Fcard-800x500.png",{"path":305,"title":867,"description":868,"published":869,"category":870,"image":871,"draft":772},"\"Database error deleting user\" in Supabase und was mitgelöscht wird","\"Database error deleting user\" in Supabase heißt: Deine Datenbank hat das Löschen abgelehnt. Der Grund steht im Log, die Lösung entscheidet, was mitgeht.","2026-10-30","Backups","\u002Fblog\u002Fdatabase-error-deleting-user\u002Fcard-800x500.png",{"path":873,"title":874,"description":875,"published":876,"category":768,"image":877,"draft":772},"\u002Fblog\u002Fstripe-webhook-no-signatures-found","\"No signatures found matching the expected signature\" bei Supabase","Ein Stripe-Webhook in einer Supabase Edge Function meldet \"No signatures found matching the expected signature\"? Zwei Ursachen. Prüfe weiter.","2026-10-29","\u002Fblog\u002Fstripe-webhook-no-signatures-found\u002Fcard-800x500.png",{"path":879,"title":880,"description":881,"published":882,"category":768,"image":883,"draft":772},"\u002Fblog\u002Fsupabase-invalid-login","\"Invalid login credentials\" in Supabase: die drei Ursachen","Supabase sagt \"Invalid login credentials\" bei falschem Passwort, unbekannter E-Mail und Google-Konto. So erkennst du, welcher Fall es ist.","2026-10-28","\u002Fblog\u002Fsupabase-invalid-login\u002Fcard-800x500.png",{"path":885,"title":886,"description":887,"published":888,"category":768,"image":889,"draft":772},"\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code","\"Edge Function returned a non-2xx status code\": der echte Fehler","\"Edge Function returned a non-2xx status code\" heißt: Deine Supabase-Funktion hat mit einem Fehler geantwortet. So liest du den echten.","2026-10-27","\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code\u002Fcard-800x500.png",{"path":891,"title":892,"description":893,"published":894,"category":768,"image":895,"draft":772},"\u002Fblog\u002Fcertificate-not-trusted","Zertifikat nicht vertrauenswürdig: drei Ursachen außer Ablauf","Zertifikat nicht vertrauenswürdig, obwohl das Datum stimmt? Es nennt eine andere Adresse, hat sich selbst signiert oder seiner Kette fehlt ein Glied.","2026-10-26","\u002Fblog\u002Fcertificate-not-trusted\u002Fcard-800x500.png",{"path":897,"title":898,"description":899,"published":900,"category":768,"image":901,"draft":772},"\u002Fblog\u002Finvalid-input-syntax-for-type-uuid","Supabase \"invalid input syntax for type uuid\": was im Zitat steht","Supabase \"invalid input syntax for type uuid\" heißt: Deine App hat etwas geschickt, das keine ID ist. Zitat lesen, Abfrage zurückhalten, Spalte behalten.","2026-10-25","\u002Fblog\u002Finvalid-input-syntax-for-type-uuid\u002Fcard-800x500.png",{"path":440,"title":903,"description":904,"published":905,"category":768,"image":906,"draft":772},"Supabase \"duplicate key value violates unique constraint\" erklärt","Supabase \"duplicate key value violates unique constraint\" heißt: Der Wert ist schon vergeben. Spalte finden, Lösung wählen, Wert vom Bildschirm fernhalten.","2026-10-24","\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint\u002Fcard-800x500.png",{"path":908,"title":909,"description":910,"published":911,"category":768,"image":912,"draft":772},"\u002Fblog\u002Fsupabase-relation-does-not-exist","Supabase \"relation does not exist\", obwohl die Tabelle da ist","Supabase \"relation does not exist\" oder PGRST205: Keine Tabelle passt zu Name und Schema deiner Abfrage. Vier Prüfungen, und was eine Freigabe öffnet.","2026-10-23","\u002Fblog\u002Fsupabase-relation-does-not-exist\u002Fcard-800x500.png",{"path":914,"title":915,"description":916,"published":917,"category":768,"image":918,"draft":772},"\u002Fblog\u002Fsupabase-function-not-found","Supabase PGRST202: \"Could not find the function in the schema cache\"","Supabase \"Could not find the function in the schema cache\" nennt die Argumente, die deine App geschickt hat. Vergleich sie mit der Funktion.","2026-10-22","\u002Fblog\u002Fsupabase-function-not-found\u002Fcard-800x500.png",{"path":213,"title":920,"description":921,"published":922,"category":768,"image":923,"draft":772},"Supabase PGRST204: \"Could not find the column in the schema cache\"","Supabase \"Could not find the column in the schema cache\" heißt meist: Der Name passt nicht. Vier Prüfungen und der Einzeiler zum Neuladen.","2026-10-21","\u002Fblog\u002Fsupabase-schema-cache-column-error\u002Fcard-800x500.png",{"path":925,"title":926,"description":927,"published":928,"category":768,"image":929,"draft":772},"\u002Fblog\u002Fsupabase-email-rate-limit-exceeded","Supabase \"Email Rate Limit Exceeded\": zwei Limits, ein Fehlercode","Supabase \"Email Rate Limit Exceeded\" begrenzt jede E-Mail deines Projekts: 2 pro Stunde beim eingebauten Absender. Was zählt und wie du es anhebst.","2026-10-20","\u002Fblog\u002Fsupabase-email-rate-limit-exceeded\u002Fcard-800x500.png",{"path":931,"title":932,"description":933,"published":934,"category":768,"image":935,"draft":772},"\u002Fblog\u002Fsupabase-users-logged-out","Supabase \"Invalid Refresh Token: Refresh Token Not Found\": 3 Ursachen","Supabase \"Invalid Refresh Token: Refresh Token Not Found\" meldet deine Nutzer ab. Drei Ursachen, und wie das Auth-Log sie trennt.","2026-10-19","\u002Fblog\u002Fsupabase-users-logged-out\u002Fcard-800x500.png",{"path":937,"title":938,"description":939,"published":940,"category":768,"image":941,"draft":772},"\u002Fblog\u002Fpermission-denied-for-schema-public","Supabase \"permission denied for schema public\": drei Ursachen","Supabase \"permission denied for schema public\" heißt: Ein Grant auf das Schema fehlt. Was ihn entfernt hat und welche Zeile ihn zurückbringt.","2026-10-18","\u002Fblog\u002Fpermission-denied-for-schema-public\u002Fcard-800x500.png",{"path":943,"title":944,"description":945,"published":946,"category":768,"image":947,"draft":772},"\u002Fblog\u002Fsupabase-bucket-not-found","Supabase \"Bucket not found\": die drei Dinge, die es bedeutet","Supabase \"Bucket not found\" ist eins von drei Dingen: ein öffentlicher Link zu einem privaten Bucket, ein ungenauer Name oder das falsche Projekt.","2026-10-17","\u002Fblog\u002Fsupabase-bucket-not-found\u002Fcard-800x500.png",{"path":949,"title":950,"description":951,"published":952,"category":768,"image":953,"draft":772},"\u002Fblog\u002Fsupabase-invalid-jwt","Supabase invalid JWT: Welcher Schlüssel streikt hier?","Ein Supabase invalid JWT ist PGRST301: ein signiertes Token hat die Prüfung nicht bestanden. In neueren Projekten ist das die Sitzung, nicht der API-Schlüssel.","2026-10-16","\u002Fblog\u002Fsupabase-invalid-jwt\u002Fcard-800x500.png",{"path":955,"title":956,"description":957,"published":958,"category":768,"image":959,"draft":772},"\u002Fblog\u002Fsupabase-single-row-error","Supabase \"multiple (or no) rows returned\" heißt meist: keine Zeilen","Supabase gibt PGRST116 zurück, \"JSON object requested, multiple (or no) rows returned\", wenn die Abfrage nichts fand. Versteckte Zeilen wirken fehlend.","2026-10-15","\u002Fblog\u002Fsupabase-single-row-error\u002Fcard-800x500.png",{"path":287,"title":961,"description":962,"published":963,"category":768,"image":964,"draft":772},"\"Database error saving new user\" in Supabase: wo du nachsiehst","\"Database error saving new user\" heißt: etwas an deiner Supabase-Registrierung ist fehlgeschlagen. Der echte Fehler steht in deinen Logs.","2026-10-14","\u002Fblog\u002Fdatabase-error-saving-new-user\u002Fcard-800x500.png",{"path":966,"title":967,"description":968,"published":969,"category":768,"image":970,"draft":772},"\u002Fblog\u002Fwhat-is-supabase","Was ist Supabase? Die Datenbank, die dein Builder ausgesucht hat","Was ist Supabase: die Datenbank, der Login und der Dateispeicher hinter deiner App. Supabase betreibt die Server, die Regeln zu deinen Zeilen gehören dir.","2026-10-13","\u002Fblog\u002Fwhat-is-supabase\u002Fcard-800x500.png",{"path":972,"title":973,"description":974,"published":975,"category":768,"image":976,"draft":772},"\u002Fblog\u002Faws-access-key-exposed-in-frontend","Ein offengelegter AWS-Schlüssel im Frontend ist kein OpenAI-Key","Ein im Frontend offengelegter AWS-Zugriffsschlüssel liest und schreibt deinen Speicher. Die Lösung ist meist eine presigned URL, kein neuer Schlüssel.","2026-10-12","\u002Fblog\u002Faws-access-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":978,"title":979,"description":980,"published":981,"category":768,"image":982,"draft":772},"\u002Fblog\u002Fstorage-upload-violates-row-level-security","\"Row-level security policy for table objects\" beim Upload","\"New row violates row-level security policy for table objects\" heißt: Deinem Upload fehlt eine insert-Regel. Ein öffentlicher Bucket fügt keine hinzu.","2026-10-11","\u002Fblog\u002Fstorage-upload-violates-row-level-security\u002Fcard-800x500.png",{"path":984,"title":985,"description":986,"published":987,"category":768,"image":988,"draft":772},"\u002Fblog\u002Fbase44-security-scan","Base44 Security Scan: das, was nur er sehen kann","Der Base44 Security Scan prüft sieben Arten von Problemen von innen. Hier ist die Hälfte, die er liest und sonst niemand, und die Hälfte, die er nie ansieht.","2026-10-10","\u002Fblog\u002Fbase44-security-scan\u002Fcard-800x500.png",{"path":990,"title":991,"description":992,"published":993,"category":768,"image":994,"draft":772},"\u002Fblog\u002Fapi-key-leaked-what-to-do","Dein API-Key wurde geleakt. Die richtige Reihenfolge","Ein API-Key wurde geleakt und du weißt nicht, was zuerst zu tun ist. Nicht jeder Key im Frontend ist ein Leck, und die Reihenfolge zählt mehr als das Tempo.","2026-10-09","\u002Fblog\u002Fapi-key-leaked-what-to-do\u002Fcard-800x500.png",{"path":996,"title":997,"description":998,"published":999,"category":768,"image":1000,"draft":772},"\u002Fblog\u002Finfinite-recursion-in-policy-for-relation","\"Infinite recursion detected in policy\" ohne RLS abzuschalten","\"Infinite recursion detected in policy for relation\" heißt: Deine Policy hat die Tabelle gefragt, die sie schützt. So brichst du den Kreis.","2026-10-08","\u002Fblog\u002Finfinite-recursion-in-policy-for-relation\u002Fcard-800x500.png",{"path":1002,"title":1003,"description":1004,"published":1005,"category":768,"image":1006,"draft":772},"\u002Fblog\u002Fno-api-key-found-in-request","\"No API key found in request\" in Supabase, und die falsche Lösung","\"No API key found in request\" heißt, deine Supabase-Anfrage kam ohne Schlüssel an. Die meisten Antworten zeigen stattdessen auf deine Datenbankregeln.","2026-10-07","\u002Fblog\u002Fno-api-key-found-in-request\u002Fcard-800x500.png",{"path":1008,"title":1009,"description":1010,"published":1011,"category":768,"image":1012,"draft":772},"\u002Fblog\u002Fis-base44-safe","Ist Base44 sicher? Was 5.442 echte Base44-Apps zeigten","Ist Base44 sicher? Wir haben 5.442 laufende Base44-Apps mit neun Checks geprüft. Fast alle bekamen ein B, aus Gründen, die der Inhaber nie gewählt hat.","2026-10-06","\u002Fblog\u002Fis-base44-safe\u002Fcard-800x500.png",{"path":637,"title":1014,"description":1015,"published":1016,"category":768,"image":1017,"draft":772},"API-Schlüssel verstecken: ab in eine Supabase Edge Function","Einen API-Schlüssel verstecken heißt, ihn aus dem Browser zu nehmen. Eine Supabase Edge Function ist der kleinste Ort dafür, und zwei Schritte zählen mehr.","2026-10-05","\u002Fblog\u002Fmove-a-secret-to-an-edge-function\u002Fcard-800x500.png",{"path":1019,"title":1020,"description":1021,"published":1022,"category":768,"image":1023,"draft":772},"\u002Fblog\u002Fenv-file-exposed-on-your-server","Ist deine .env-Datei öffentlich? Zwölf Pfade zum Prüfen","Ist deine .env-Datei auf deinem eigenen Webserver öffentlich? Zwölf Adressen sagen es dir, und ein Treffer heißt: alles darin ist längst öffentlich.","2026-10-04","\u002Fblog\u002Fenv-file-exposed-on-your-server\u002Fcard-800x500.png",{"path":1025,"title":1026,"description":1027,"published":1028,"category":768,"image":1029,"draft":772},"\u002Fblog\u002Fis-v0-safe","v0-Sicherheit: alle 1.790 gescannten v0-Apps bekamen ein A","v0-Sicherheit, gemessen an 1.790 laufenden v0-Apps: Jede bekam ein A. Nur 17 nannten eine Datenbank, und genau das misst das A vor allem.","2026-10-03","\u002Fblog\u002Fis-v0-safe\u002Fcard-800x500.png",{"path":1031,"title":1032,"description":1033,"published":1034,"category":768,"image":1035,"draft":772},"\u002Fblog\u002Fis-bolt-safe","Ist Bolt sicher? Was 1.123 echte Bolt-Apps zeigten","Ist Bolt sicher? Wir haben 1.123 laufende Bolt-Apps mit neun Checks geprüft. Das Hosting war sauber. Die Funde waren Schlüssel und offene Tabellen in den Apps.","2026-10-02","\u002Fblog\u002Fis-bolt-safe\u002Fcard-800x500.png",{"path":1037,"title":1038,"description":1039,"published":1040,"category":768,"image":1041,"draft":772},"\u002Fblog\u002Fis-supabase-down-or-your-app","Ist Supabase down oder liegt es an deiner App? So erkennst du es","Ist Supabase down oder liegt es an deiner App? Ein pausiertes Projekt, eine volle Datenbank und ein eigener Fehler sehen gleich aus. Vier Prüfungen helfen.","2026-10-01","\u002Fblog\u002Fis-supabase-down-or-your-app\u002Fcard-800x500.png",{"path":1043,"title":1044,"description":1045,"published":1046,"category":768,"image":1047,"draft":772},"\u002Fblog\u002Fdoes-supabase-encrypt-my-data","Verschlüsselt Supabase meine Daten? Ja. Wovor das schützt","Verschlüsselt Supabase Daten? Ja: AES-256 im Ruhezustand, TLS bei der Übertragung, SOC 2 und ISO 27001. Was jedes abdeckt und welches Leck keines stoppt.","2026-09-30","\u002Fblog\u002Fdoes-supabase-encrypt-my-data\u002Fcard-800x500.png",{"path":1049,"title":1050,"description":1051,"published":1052,"category":768,"image":1053,"draft":772},"\u002Fblog\u002Fsupabase-free-plan-limits","Supabase Gratis-Tarif: die Limits und was an jedem passiert","Die Limits des Supabase-Gratis-Tarifs: 500 MB Datenbank, 5 GB Egress und keine Backups. Was an jeder Grenze passiert und was $25 für Pro ändern.","2026-09-29","\u002Fblog\u002Fsupabase-free-plan-limits\u002Fcard-800x500.png",{"path":1055,"title":1056,"description":1057,"published":1058,"category":870,"image":1059,"draft":772},"\u002Fblog\u002Ftest-your-supabase-backup","Teste dein Supabase-Backup, bevor du es brauchst","So testest du dein Supabase-Backup: in ein Übungsprojekt einspielen, Zeilen zählen, anmelden und die Zeile suchen, die einer abgeschnittenen Datei fehlt.","2026-09-28","\u002Fblog\u002Ftest-your-supabase-backup\u002Fcard-800x500.png",{"path":1061,"title":1062,"description":1063,"published":1064,"category":870,"image":1065,"draft":772},"\u002Fblog\u002Fsupabase-backup-github-action","Kostenloses Supabase-Backup per GitHub Action, mit Haken","Eine Supabase-Backup-GitHub-Action kostet nichts und passt für viele Apps. Der Workflow, der richtige Connection String und das Egress pro Lauf.","2026-09-27","\u002Fblog\u002Fsupabase-backup-github-action\u002Fcard-800x500.png",{"path":1067,"title":1068,"description":1069,"published":1070,"category":870,"image":1071,"draft":772},"\u002Fblog\u002Fdownload-your-supabase-backup","Warum du dein Supabase-Backup nicht herunterladen kannst","Dein Supabase-Backup kannst du auf aktuellen Projekten nicht herunterladen, weil die Tageskopie physisch ist. Wie du es prüfst und selbst eine Kopie hältst.","2026-09-26","\u002Fblog\u002Fdownload-your-supabase-backup\u002Fcard-800x500.png",{"path":1073,"title":1074,"description":1075,"published":1070,"category":768,"image":1076,"draft":772},"\u002Fblog\u002Fsupabase-permission-denied-for-table","Supabase \"permission denied for table\": der fehlende Grant","Ab 30. Oktober antwortet eine neue Supabase-Tabelle mit \"permission denied for table\", bis du Zugriff erteilst. Der Grant aus der E-Mail ist die halbe Lösung.","\u002Fblog\u002Fsupabase-permission-denied-for-table\u002Fcard-800x500.png",{"path":1078,"title":1079,"description":1080,"published":1081,"category":768,"image":1082,"draft":772},"\u002Fblog\u002Fopen-api-endpoint-exposed","Ist ein offener API-Endpunkt ein Sicherheitsproblem? Sieh ins JSON","Dein Scan hat einen offenen API-Endpunkt gemeldet. Ob das zählt, hängt davon ab, was zurückkam, und die meisten hier gehörten der Plattform.","2026-09-25","\u002Fblog\u002Fopen-api-endpoint-exposed\u002Fcard-800x500.png",{"path":1084,"title":1085,"description":1086,"published":1087,"category":768,"image":1088,"draft":772},"\u002Fblog\u002Flovable-security-scan","Lovable Security Scan: das Eine, was er nicht beweist","Lovable Security Scan: was Quick Scan und Deep Scan prüfen, wann welcher läuft, und das Eine, was kein Scan von innen beweisen kann.","2026-09-24","\u002Fblog\u002Flovable-security-scan\u002Fcard-800x500.png",{"path":1090,"title":1091,"description":1092,"published":1093,"category":870,"image":1094,"draft":772},"\u002Fblog\u002Fsupabase-backup-auth-users","Warum in deinem Supabase-Dump keine Nutzer stehen","Führe supabase db dump allein aus und du bekommst die Form deiner Datenbank und keine einzige Zeile, und das auth-Schema mit deinen Nutzern fehlt ganz.","2026-09-23","\u002Fblog\u002Fsupabase-backup-auth-users\u002Fcard-800x500.png",{"path":1096,"title":1097,"description":1098,"published":1099,"category":768,"image":1100,"draft":772},"\u002Fblog\u002Fdomain-and-certificate-expiry","Domain abgelaufen, Website offline: was jetzt passiert","Deine Domain ist abgelaufen und deine Website ist offline. Welche Frist jetzt läuft, warum das Zertifikat das kleinere Problem ist, und wie du beides prüfst.","2026-09-22","\u002Fblog\u002Fdomain-and-certificate-expiry\u002Fcard-800x500.png",{"path":1102,"title":1103,"description":1104,"published":1105,"category":768,"image":1106,"draft":772},"\u002Fblog\u002Fis-lovable-safe","Ist Lovable sicher? Was 18.554 echte Lovable-Apps zeigten","Ist Lovable sicher? Wir haben 18.554 laufende Lovable-Apps mit neun Checks geprüft. Die Plattform war die sauberste von fünf. Die Funde saßen in den Apps.","2026-09-21","\u002Fblog\u002Fis-lovable-safe\u002Fcard-800x500.png",{"path":1108,"title":1109,"description":1110,"published":1111,"category":768,"image":1112,"draft":772},"\u002Fblog\u002Fvibe-coded-app-security-checklist","Die Sicherheits-Checkliste für Vibe-Coding, in neun Prüfungen","Eine Sicherheits-Checkliste für Vibe-Coding mit neun Punkten, die jeder von außen an deiner Live-App nachprüfen kann, jeder mit einem Test in einer Zeile.","2026-09-20","\u002Fblog\u002Fvibe-coded-app-security-checklist\u002Fcard-800x500.png",{"path":1114,"title":1115,"description":1116,"published":1117,"category":768,"image":1118,"draft":772},"\u002Fblog\u002Fstripe-secret-key-in-frontend","Ein geheimer Stripe-Schlüssel im Frontend kann Geld bewegen","Ein geheimer Stripe-Schlüssel im Frontend kann erstatten, abbuchen und jeden Kundendatensatz lesen, den du hast. Dein pk_live_-Schlüssel gehört dorthin.","2026-09-19","\u002Fblog\u002Fstripe-secret-key-in-frontend\u002Fcard-800x500.png",{"path":1120,"title":1121,"description":1122,"published":1123,"category":768,"image":1124,"draft":772},"\u002Fblog\u002Fvite-and-next-public-env-vars","Vite-Env-Variablen offengelegt: der Präfix heißt veröffentlichen","Vite-Env-Variablen offengelegt: Deine App hat getan, was der Präfix verlangt. VITE_ und NEXT_PUBLIC_ heißen veröffentlichen, und die KI kannte den Preis nie.","2026-09-18","\u002Fblog\u002Fvite-and-next-public-env-vars\u002Fcard-800x500.png",{"path":1126,"title":1127,"description":1128,"published":1129,"category":768,"image":1130,"draft":772},"\u002Fblog\u002Fbase44-source-maps","Base44-Sicherheit: was ein Scan meldet und was du ändern kannst","Base44-Sicherheit auf 5.438 gescannten Apps: Drei Befunde auf fast jeder gehören der Plattform, die Source Map ist das Base44-Badge, und deine Liste ist kurz.","2026-09-17","\u002Fblog\u002Fbase44-source-maps\u002Fcard-800x500.png",{"path":1132,"title":1133,"description":1134,"published":1135,"category":768,"image":1136,"draft":772},"\u002Fblog\u002Fis-cursor-ai-safe","Ist Cursor AI sicher? Editor, Code und die App, die du veröffentlichst","Ist Cursor AI sicher? Drei Fragen in einer Suche: was Cursor behält, was sein Code falsch macht und ob deine veröffentlichte App offen steht.","2026-09-16","\u002Fblog\u002Fis-cursor-ai-safe\u002Fcard-800x500.png",{"path":1138,"title":1139,"description":1140,"published":1141,"category":768,"image":1142,"draft":772},"\u002Fblog\u002Fis-replit-safe","Ist Replit sicher? Was wir in 3.042 Replit-Apps gefunden haben","Ist Replit sicher? Wir haben 3.042 laufende Replit-Apps mit neun externen Checks geprüft. Die Funde saßen in der veröffentlichten App, nicht im Hosting.","2026-09-15","\u002Fblog\u002Fis-replit-safe\u002Fcard-800x500.png",{"path":1144,"title":1145,"description":1146,"published":1147,"category":870,"image":1148,"draft":772},"\u002Fblog\u002Fsupabase-storage-backup","Supabase Storage Backup: deine Datenbankkopie hat keine Dateien","Ein Supabase Storage Backup ist eine eigene Aufgabe. Datenbank-Backups enthalten die Liste deiner Dateien und keine davon, also bleibt jeder Upload kaputt.","2026-09-14","\u002Fblog\u002Fsupabase-storage-backup\u002Fcard-800x500.png",{"path":1150,"title":1151,"description":1152,"published":1153,"category":870,"image":1154,"draft":772},"\u002Fblog\u002Fsupabase-point-in-time-recovery","Supabase Point-in-Time Recovery: Kosten und Grenzen","Supabase Point-in-Time Recovery spult deine Datenbank auf jede Sekunde der letzten Woche zurück. $100 im Monat zusätzlich zu Pro, und nur die Datenbank.","2026-09-13","\u002Fblog\u002Fsupabase-point-in-time-recovery\u002Fcard-800x500.png",{"path":1156,"title":1157,"description":1158,"published":1159,"category":870,"image":1160,"draft":772},"\u002Fblog\u002Fsupabase-project-paused-recover","Supabase-Projekt pausiert? Deine Daten sind noch da","Supabase hat dein Projekt nach einer Woche ohne Aktivität pausiert. Gelöscht ist nichts, Wiederherstellen steht neben dem Namen, und du hast ein Jahr.","2026-09-12","\u002Fblog\u002Fsupabase-project-paused-recover\u002Fcard-800x500.png",{"path":1162,"title":1163,"description":1164,"published":1165,"category":768,"image":1166,"draft":772},"\u002Fblog\u002Fsafest-ai-app-builder","Welcher KI-App-Builder ist am sichersten? 30.998 Apps im Test","Welcher KI-App-Builder ist am sichersten? Wir haben 30.998 aktive Apps von Lovable, Base44, Replit, v0 und Bolt gescannt. Der Builder entscheidet das nicht.","2026-09-11","\u002Fblog\u002Fsafest-ai-app-builder\u002Fcard-800x500.png",{"path":589,"title":1168,"description":1169,"published":1170,"category":768,"image":1171,"draft":772},"Row Level Security für jede Supabase-Tabelle aktivieren und prüfen","Row Level Security in Supabase ohne Policy sperrt eine Tabelle komplett. Eine Policy ohne den Schalter bringt gar nichts. Hier ist das SQL, und der Test.","2026-09-10","\u002Fblog\u002Fenable-rls-on-every-supabase-table\u002Fcard-800x500.png",{"path":1173,"title":1174,"description":1175,"published":1176,"category":768,"image":1177,"draft":772},"\u002Fblog\u002Fsupabase-rls-disabled-in-public","Supabase \"RLS disabled in public\": was die Warnung übersieht","Supabase meldet \"RLS disabled in public\" als Fehler. Zur Lese-Policy, die deine Tabelle genauso offen lässt, sagt der Advisor gar nichts.","2026-09-09","\u002Fblog\u002Fsupabase-rls-disabled-in-public\u002Fcard-800x500.png",{"path":1179,"title":1180,"description":1181,"published":1182,"category":768,"image":1183,"draft":772},"\u002Fblog\u002Frotate-supabase-service-role-key","Geleakten Supabase-service_role-Schlüssel richtig tauschen","Supabase sagt: erst das Leck schließen. Andere Anleitungen: sofort tauschen. Was richtig ist, hängt davon ab, wohin dein service_role-Schlüssel gelangt ist.","2026-09-08","\u002Fblog\u002Frotate-supabase-service-role-key\u002Fcard-800x500.png",{"path":1185,"title":1186,"description":1187,"published":1188,"category":768,"image":1189,"draft":772},"\u002Fblog\u002Fvibe-coding-security-scanners-compared","Vibe-Coding-Security-Scanner im Vergleich, unserer eingeschlossen","Der beste Vibe-Coding-Security-Scanner hängt an drei Fragen, die keine Funktionsliste beantwortet. Acht Tools im Vergleich, mit Preisen und unseren Lücken.","2026-09-07","\u002Fblog\u002Fvibe-coding-security-scanners-compared\u002Fcard-800x500.png",{"path":1191,"title":1192,"description":1193,"published":1194,"category":768,"image":1195,"draft":772},"\u002Fblog\u002Fsupabase-security-checker","Supabase Security Checker: die fünf Prüfungen selbst machen","Ein Supabase Security Checker liest deine veröffentlichte App statt deiner Projekteinstellungen. Hier sind die fünf Prüfungen und wie du jede selbst ausführst.","2026-09-06","\u002Fblog\u002Fsupabase-security-checker\u002Fcard-800x500.png",{"path":1197,"title":1198,"description":1199,"published":1200,"category":768,"image":1201,"draft":772},"\u002Fblog\u002Fvibe-coding-security-scanner","Vibe-Coding-Security-Scanner: was ein URL-Scan übersieht","Ein Vibe-Coding-Security-Scanner liest deine laufende App von außen. Was das abdeckt, die vier Dinge, die er nicht sehen kann, und wie du das Ergebnis liest.","2026-09-05","\u002Fblog\u002Fvibe-coding-security-scanner\u002Fcard-800x500.png",{"path":1203,"title":1204,"description":1205,"published":1206,"category":768,"image":1207,"draft":772},"\u002Fblog\u002Fmissing-security-headers","Fehlende Sicherheitsheader: wann sie wirklich zählen","Fehlende Sicherheitsheader ist unser häufigster Befund. Wogegen sie schützen, und wann sie die unwichtigste Zeile in deinem Bericht sind.","2026-09-04","\u002Fblog\u002Fmissing-security-headers\u002Fcard-800x500.png",{"path":1209,"title":1210,"description":1211,"published":1212,"category":768,"image":1213,"draft":772},"\u002Fblog\u002Fopenai-api-key-exposed-in-frontend","Dein OpenAI-API-Schlüssel liegt im Frontend. Tausch ihn aus.","Ein OpenAI-API-Schlüssel im Frontend lässt sich an keine Domain binden. Tausch ihn heute, leg den Aufruf hinter deinen Server und begrenze das Budget.","2026-09-03","\u002Fblog\u002Fopenai-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":1215,"title":1216,"description":1217,"published":1218,"category":768,"image":1219,"draft":772},"\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps","API-Schlüssel im Frontend: was 30.998 Apps wirklich ausliefern","Ein API-Schlüssel im Frontend ist meistens ein Google-Maps-Schlüssel. Wir haben 30.998 aktive Vibe-Coding-Apps gescannt und gezählt, was wirklich austritt.","2026-09-02","\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps\u002Fcard-800x500.png",{"path":1221,"title":1222,"description":1223,"published":1224,"category":870,"image":1225,"draft":772},"\u002Fblog\u002Fsupabase-backup-tools-compared","Supabase-Backup-Tools im Vergleich, unseres eingeschlossen","Vier Arten von Supabase-Backup-Tool, was jedes wirklich kopiert und wann eine kostenlose GitHub Action jeden Anbieter schlägt, uns eingeschlossen.","2026-09-01","\u002Fblog\u002Fsupabase-backup-tools-compared\u002Fcard-800x500.png",{"path":1227,"title":1228,"description":1229,"published":1230,"category":768,"image":1231,"draft":772},"\u002Fblog\u002Freplit-secrets-explained","Secrets in Replit nutzen, und was trotzdem veröffentlicht wird","Secrets in Replit nutzen: anlegen, auslesen und die zwei Gründe beheben, warum undefined zurückkommt. Dazu die Schlüssel, die das Werkzeug nicht schützt.","2026-08-31","\u002Fblog\u002Freplit-secrets-explained\u002Fcard-800x500.png",{"path":1233,"title":1234,"description":1235,"published":1236,"category":870,"image":1237,"draft":772},"\u002Fblog\u002Fback-up-supabase-free-tier","Supabase Gratis-Tarif: Backup ohne Terminal selbst machen","Im Supabase Gratis-Tarif gibt es keine Backups, die Kopie musst du selbst machen. Wie das im Dashboard geht und was der CSV-Weg auslässt.","2026-08-30","\u002Fblog\u002Fback-up-supabase-free-tier\u002Fcard-800x500.png",{"path":1239,"title":1240,"description":1241,"published":1242,"category":768,"image":1243,"draft":772},"\u002Fblog\u002Fis-supabase-secure","Ist Supabase sicher? Ja. Dein Projekt ist eine eigene Frage","Ist Supabase sicher? Die Plattform ist auditiert, verschlüsselt und pentestet. Supabase schreibt selbst auf, wo das aufhört und deine Einstellungen anfangen.","2026-08-29","\u002Fblog\u002Fis-supabase-secure\u002Fcard-800x500.png",{"path":1245,"title":1246,"description":1247,"published":1248,"category":768,"image":1249,"draft":772},"\u002Fblog\u002Fwhere-to-find-supabase-api-keys","Wo du deine Supabase API-Keys findest: anon, service_role, URL","Projekt-URL, anon-Schlüssel und service_role-Schlüssel liegen auf einer Seite im Dashboard. Wo sie ist, und welcher der vier Werte in deine App gehört.","2026-08-28","\u002Fblog\u002Fwhere-to-find-supabase-api-keys\u002Fcard-800x500.png",{"path":533,"title":1251,"description":1252,"published":1253,"category":768,"image":1254,"draft":772},"New row violates row-level security policy in Supabase: was jetzt?","\"New row violates row-level security policy\" heißt, Supabase hat einen Schreibvorgang abgelehnt. Die schnelle Lösung öffnet die Tabelle wieder für alle.","2026-08-27","\u002Fblog\u002Fnew-row-violates-row-level-security-policy\u002Fcard-800x500.png",{"path":1256,"title":1257,"description":1258,"published":1259,"category":768,"image":1260,"draft":772},"\u002Fblog\u002Fcors-wildcard-security-risk","Ist eine CORS-Wildcard ein Sicherheitsrisiko? Meist nicht.","Ist eine CORS-Wildcard ein Sicherheitsrisiko? Meist ist sie die Voreinstellung deines Builders und gibt nichts preis, was dein Server nicht ohnehin herausgab.","2026-08-26","\u002Fblog\u002Fcors-wildcard-security-risk\u002Fcard-800x500.png",{"path":1262,"title":1263,"description":1264,"published":1265,"category":870,"image":1266,"draft":772},"\u002Fblog\u002Fsupabase-branching-is-not-a-backup","Supabase Branching ist kein Backup. Es geht nur vorwärts.","Warum Supabase Branching kein Backup ist: Ein Branch startet ohne deine Daten, und ein Merge bringt nur das Schema. Wofür es gut ist und was stattdessen hilft.","2026-08-25","\u002Fblog\u002Fsupabase-branching-is-not-a-backup\u002Fcard-800x500.png",{"path":1268,"title":1269,"description":1270,"published":1271,"category":870,"image":1272,"draft":772},"\u002Fblog\u002Fhow-to-restore-a-supabase-backup","Supabase-Backup wiederherstellen, und was danach kaputt ist","Wie du ein Supabase-Backup aus dem Dashboard oder aus einer Dump-Datei wiederherstellst, was ersetzt wird und warum die App danach kaputt sein kann.","2026-08-24","\u002Fblog\u002Fhow-to-restore-a-supabase-backup\u002Fcard-800x500.png",{"path":1274,"title":1275,"description":1276,"published":1277,"category":768,"image":1278,"draft":772},"\u002Fblog\u002Fsupabase-storage-bucket-public","Dein Supabase-Storage-Bucket ist öffentlich. Ist das ein Problem?","Ein öffentlicher Supabase-Storage-Bucket heißt: Wer die URL einer Datei hat, kann sie öffnen. Er heißt nicht, dass jemand den Inhalt auflisten darf.","2026-08-23","\u002Fblog\u002Fsupabase-storage-bucket-public\u002Fcard-800x500.png",{"path":1280,"title":1281,"description":1282,"published":1283,"category":768,"image":1284,"draft":772},"\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend","Ist ein Google-API-Schlüssel im Frontend ein Problem?","Ein Google-API-Schlüssel im Frontend ist der Fund unseres Scanners Nummer eins, und meistens unbedenklich. Eine kostenlose Einstellung entscheidet darüber.","2026-08-22","\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":1286,"title":1287,"description":1288,"published":1289,"category":870,"image":1290,"draft":772},"\u002Fblog\u002Fai-agent-deleted-my-database","Ein KI-Agent hat meine Supabase-Daten gelöscht. Was hole ich zurück?","Ein KI-Agent hat Daten aus deiner Datenbank gelöscht. Was du zurückholst, stand schon vorher fest, und die nächsten Minuten entscheiden über den Rest.","2026-08-21","\u002Fblog\u002Fai-agent-deleted-my-database\u002Fcard-800x500.png",{"path":1292,"title":1293,"description":1294,"published":1295,"category":768,"image":1296,"draft":772},"\u002Fblog\u002Fcan-anyone-read-your-supabase-database","Kann jeder deine Supabase-Datenbank lesen? 3.680 Apps im Test","Kann jeder deine Supabase-Datenbank ohne Login lesen? Wir haben 30.998 aktive Apps aus KI-Buildern gescannt und gemessen, wie oft die Antwort ja lautet.","2026-08-18","\u002Fblog\u002Fcan-anyone-read-your-supabase-database\u002Fcard-800x500.png",{"path":1298,"title":1299,"description":1300,"published":1295,"category":768,"image":1301,"draft":772},"\u002Fblog\u002Fsource-maps-exposed-in-production","Source-Maps öffentlich: deine App veröffentlicht ihren Quellcode","Eine öffentliche Source-Map lässt jeden den Originalcode deiner App lesen, Kommentare inklusive. Der 30-Sekunden-Check, und was wirklich zählt.","\u002Fblog\u002Fsource-maps-exposed-in-production\u002Fcard-800x500.png",{"path":1303,"title":1304,"description":1305,"published":1306,"category":768,"image":1307,"draft":772},"\u002Fblog\u002Fsupabase-new-api-keys","Supabase publishable key: ist er derselbe wie der anon key?","Supabase hat anon und service_role durch öffentliche und geheime Schlüssel ersetzt. Welcher gehört in deine App, und welcher nie?","2026-08-12","\u002Fblog\u002Fsupabase-new-api-keys\u002Fcard-800x500.png",{"path":1309,"title":1310,"description":1311,"published":1312,"category":870,"image":1313,"draft":772},"\u002Fblog\u002Fdoes-supabase-back-up-my-database","Sichert Supabase meine Datenbank? Das hängt von deinem Tarif ab.","Sichert Supabase deine Datenbank? Auf bezahlten Tarifen täglich, im Gratis-Tarif gar nicht. So findest du heraus, was du hast und was es nicht abdeckt.","2026-08-11","\u002Fblog\u002Fdoes-supabase-back-up-my-database\u002Fcard-800x500.png",{"path":1315,"title":1316,"description":1317,"published":1318,"category":768,"image":1319,"draft":772},"\u002Fblog\u002Fsupabase-rls-on-but-table-still-public","Supabase Row Level Security ist an. Die Tabelle ist trotzdem offen.","Supabase Row Level Security anzuschalten schützt keine Tabelle. Erst deine Policies tun das, und die, die deine App repariert hat, lässt oft alle herein.","2026-08-10","\u002Fblog\u002Fsupabase-rls-on-but-table-still-public\u002Fcard-800x500.png",{"path":1321,"title":1322,"description":1323,"published":1318,"category":870,"image":1324,"draft":772},"\u002Fblog\u002Fversion-history-is-not-a-backup","Der Versionsverlauf ist kein Backup. Er holt keine Tabelle zurück.","Lovable und Bolt führen einen Versionsverlauf für deinen Code. Deine Datenbank ist ein eigener Dienst, und ein Rücksprung holt deine Daten nicht zurück.","\u002Fblog\u002Fversion-history-is-not-a-backup\u002Fcard-800x500.png",{"path":1326,"title":1327,"description":1328,"published":1329,"category":870,"image":1330,"draft":772},"\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database","Drei Wege, eine Supabase-Datenbank zu sichern, und was fehlt","Das Dashboard, pg_dump und ein verwalteter Dienst. Was jeder Weg wirklich sichert, was er stillschweigend auslässt und welcher den Kontoverlust übersteht.","2026-08-09","\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database\u002Fcard-800x500.png",{"path":1332,"title":1333,"description":1334,"published":1329,"category":768,"image":1335,"draft":772},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","Welche API-Schlüssel im Frontend sicher sind, und welche nicht","Dein Supabase-anon-Schlüssel darf öffentlich sein. Der service_role-Schlüssel nicht. Er ignoriert jede Regel, die du setzt. So erkennst du den Unterschied.","\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend\u002Fcard-800x500.png",1794213638640]