[{"data":1,"prerenderedAt":1229},["ShallowReactive",2],{"blog-de-violates-foreign-key-constraint":3,"blog-index-de":701},{"id":4,"title":5,"body":6,"category":660,"cover":661,"coverAlt":662,"description":663,"draft":664,"extension":665,"faq":666,"image":682,"keywords":683,"meta":690,"navigation":691,"ogTitle":5,"path":692,"published":693,"seo":694,"stem":695,"tldr":696,"updated":693,"__hash__":700},"blog_de\u002Fblog\u002Fviolates-foreign-key-constraint.md","Supabase-Fehler \"violates foreign key constraint\" beim Einfügen",{"type":7,"value":8,"toc":647},"minimark",[9,13,24,31,34,39,50,53,170,179,191,195,198,222,235,248,251,259,266,272,275,279,282,288,296,313,319,342,352,388,392,395,402,408,422,426,440,446,451,455,458,465,471,481,484,490,498,504,508,514,531,534,540,543,547,550,559,581,590,604,608,640],[10,11,12],"p",{},"Du drückst in deiner App bei einem neuen Profil auf Speichern, und nichts ändert\nsich. Die Seite zeigt keine Meldung. Die Browserkonsole hat eine:",[14,15,20],"pre",{"className":16,"code":18,"language":19},[17],"language-text","{\n  \"code\": \"23503\",\n  \"details\": \"Key is not present in table \\\"users\\\".\",\n  \"hint\": null,\n  \"message\": \"insert or update on table \\\"profiles\\\" violates foreign key constraint \\\"profiles_id_fkey\\\"\"\n}\n","text",[21,22,18],"code",{"__ignoreMap":23},"",[10,25,26,27,30],{},"Supabase \"violates foreign key constraint\" heißt, dass die Zeile, die du speichern\nwolltest, eine Zeile in einer anderen Tabelle nennt, und diese Zeile gibt es nicht.\nHier trug das Profil eine ",[21,28,29],{},"id",", die zu jemandem gehören sollte, der sich\nregistriert hat, und Postgres hat niemanden mit dieser id gefunden. Gespeichert\nwurde nichts.",[10,32,33],{},"Am schnellsten verschwindet die Meldung, wenn du die Regel löschst. Danach gehen\ndie Speichervorgänge durch, und die Zeilen, die sie anlegen, zeigen ins Leere: eine\nBestellposition ohne Bestellung, ein Profil ohne Person dahinter. Finde zuerst\nheraus, welche Zeile fehlt und warum. Sie ist auf einem von wenigen Wegen\nverloren gegangen.",[35,36,38],"h2",{"id":37},"was-bedeutet-violates-foreign-key-constraint-in-supabase","Was bedeutet \"violates foreign key constraint\" in Supabase?",[10,40,41,42,49],{},"Eine Spalte deiner Zeile muss zu einer Zeile in einer anderen Tabelle passen, der\nWert, den du geschickt hast, passt zu keiner, und Postgres hat die ganze Zeile\nabgelehnt. Die\n",[43,44,48],"a",{"href":45,"rel":46},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fddl-constraints.html",[47],"nofollow","Dokumentation von Postgres zu Constraints","\nbeschreibt die Regel in einem Satz: Ein Fremdschlüssel \"specifies that the values\nin a column (or a group of columns) must match the values appearing in some row of\nanother table.\"",[10,51,52],{},"Stell dir die andere Tabelle als Straße vor und jede ihrer Zeilen als Haus mit\neiner Nummer. Deine neue Zeile ist ein Brief, und die Spalte mit dem Fremdschlüssel\nist die Hausnummer darauf. Postgres ist das Briefzentrum. Bevor es den Brief\nannimmt, schlägt es die Nummer in der Straße nach, und ein Brief an ein Haus, das\nes nicht gibt, kommt sofort zurück. Die Meldung sagt, welcher Brief zurückkam. Die\nDetailzeile sagt, in welcher Straße gesucht wurde.",[54,55,56,72],"table",{},[57,58,59],"thead",{},[60,61,62,66,69],"tr",{},[63,64,65],"th",{},"Feld",[63,67,68],{},"Beispiel",[63,70,71],{},"Was es dir sagt",[73,74,75,99,114,130,145],"tbody",{},[60,76,77,82,87],{},[78,79,80],"td",{},[21,81,21],{},[78,83,84],{},[21,85,86],{},"23503",[78,88,89,90,93,94],{},"Der Code von Postgres für diesen Fehler, ",[21,91,92],{},"foreign_key_violation"," in ",[43,95,98],{"href":96,"rel":97},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Futils\u002Ferrcodes.txt",[47],"seiner Liste der Codes",[60,100,101,106,111],{},[78,102,103],{},[21,104,105],{},"message",[78,107,108],{},[21,109,110],{},"insert or update on table \"profiles\" violates foreign key constraint \"profiles_id_fkey\"",[78,112,113],{},"Die Tabelle, in die du geschrieben hast, dann der Name der Regel",[60,115,116,121,127],{},[78,117,118],{},[21,119,120],{},"details",[78,122,123,126],{},[21,124,125],{},"Key (id)=(…) is not present in table \"users\"."," oder dasselbe ohne den Wert",[78,128,129],{},"Die Spalte, der Wert, den du geschickt hast, und die Tabelle, in der Postgres gesucht hat. Der nächste Abschnitt zeigt, wann der Wert fehlt",[60,131,132,137,142],{},[78,133,134],{},[21,135,136],{},"hint",[78,138,139],{},[21,140,141],{},"null",[78,143,144],{},"Nichts hinzuzufügen bei diesem Fehler",[60,146,147,150,155],{},[78,148,149],{},"HTTP-Status",[78,151,152],{},[21,153,154],{},"409 Conflict",[78,156,157,158,161,162,93,164,169],{},"PostgREST, der Teil von Supabase, der Aufrufe ",[21,159,160],{},"supabase.from(...)"," in SQL übersetzt, ordnet ",[21,163,86],{},[43,165,168],{"href":166,"rel":167},"https:\u002F\u002Fgithub.com\u002FPostgREST\u002Fpostgrest\u002Fblob\u002Fmain\u002Fsrc\u002Flibrary\u002FPostgREST\u002FError.hs",[47],"seinem Code"," dem Status 409 zu",[10,171,172,173,178],{},"Der Wortlaut stammt aus\n",[43,174,177],{"href":175,"rel":176},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Futils\u002Fadt\u002Fri_triggers.c",[47],"der Datei in Postgres, die diese Prüfungen ausführt",".\nAuch ein Update löst ihn aus, wenn es die Spalte auf eine Nummer ohne Haus setzt,\nund deshalb beginnt die Meldung mit \"insert or update\".",[10,180,181,182,185,186,190],{},"Eine Meldung, die mit ",[21,183,184],{},"update or delete on table"," beginnt, ist dieselbe Regel von\nder Straße aus gesehen: Du wolltest ein Haus abreißen, auf das noch Briefe zeigen.\nSie kommt vom Löschen, und\n",[43,187,189],{"href":188},"\u002Fblog\u002Fdatabase-error-deleting-user","der Beitrag über das Löschen von Nutzern","\nbehandelt sie.",[35,192,194],{"id":193},"nach-welcher-zeile-sucht-postgres","Nach welcher Zeile sucht Postgres?",[10,196,197],{},"Nach der aus der Detailzeile, in der Tabelle, die diese Zeile nennt. Zwei Dinge\nmachen die Zeile schwerer zu lesen, als sie aussieht.",[10,199,200,204,205,208,209,213,214,217,218,221],{},[201,202,203],"strong",{},"Manchmal fehlt der Wert."," Postgres gibt ",[21,206,207],{},"Key (id)=(…)"," nur aus, wenn der\nAufrufer diese Spalte deiner Tabelle lesen darf. Ist Row Level Security auf der\nTabelle eingeschaltet, in die du geschrieben hast, lässt\n",[43,210,212],{"href":175,"rel":211},[47],"sein Code","\nden Wert weg, mit dem Kommentar \"we don't return any specifics to avoid leaking\ndata\", und du bekommst ",[21,215,216],{},"Key is not present in table \"users\"."," Das sieht eine\nBrowseranfrage an eine geschützte Tabelle. Der Wert ist der, den deine App\ngeschickt hat, also öffne die Entwicklertools deines Browsers, such im\nNetzwerk-Tab die Anfrage an ",[21,219,220],{},"\u002Frest\u002Fv1\u002Fprofiles"," und lies ihn aus dem Payload.",[10,223,224,225,229,230,234],{},"Zeigt deine Konsole den Wert bei einer Anfrage, die deine App mit dem öffentlichen\nSchlüssel gestellt hat, ist Row Level Security auf der Tabelle aus, in die du\ngeschrieben hast. Eine Tabelle mit ausgeschalteten Regeln gibt einem Fremden ihre\nZeilen genauso bereitwillig wie dir, und\n",[43,226,228],{"href":227},"\u002Fblog\u002Fenable-rls-on-every-supabase-table","die Regeln für jede Tabelle einschalten","\nzeigt den Schalter und die erste Regel. Unser ",[43,231,233],{"href":232},"\u002Fsecurity-scanner","kostenloser Scan","\nliest deine Live-App mit dem öffentlichen Schlüssel, den jeder Besucher bekommt,\nund listet die Tabellen auf, die ihm antworten. Er braucht kein Konto.",[10,236,237,204,240,243,244,247],{},[201,238,239],{},"Der Tabellenname hat kein Schema.",[21,241,242],{},"users"," aus, und in einem\nSupabase-Projekt zeigt eine Spalte, die auf Personen zeigt, fast immer auf\n",[21,245,246],{},"auth.users",", die Tabelle, in der Supabase Auth alle registrierten Personen führt.\nSie steht nicht unter deinen öffentlichen Tabellen im Table Editor, und deshalb\nliest sich die Meldung, als nenne sie eine Tabelle, die du nie angelegt hast.",[10,249,250],{},"Um genau zu sehen, wohin die Regel zeigt, führ das im SQL Editor aus, mit dem\nNamen der Regel aus der Meldung:",[14,252,257],{"className":253,"code":255,"language":256,"meta":23},[254],"language-sql","select conrelid::regclass as on_table,\n       pg_get_constraintdef(oid) as rule\nfrom pg_constraint\nwhere conname = 'profiles_id_fkey';\n","sql",[21,258,255],{"__ignoreMap":23},[10,260,261,262,265],{},"Die Antwort nennt die Tabelle, auf der die Regel sitzt, und was die Regel sagt,\nzum Beispiel ",[21,263,264],{},"FOREIGN KEY (id) REFERENCES auth.users(id) ON DELETE CASCADE",": deine\nSpalte, dann die Tabelle und Spalte, zu der sie passen muss. Dann schlag den Wert\nnach:",[14,267,270],{"className":268,"code":269,"language":256,"meta":23},[254],"select id, email, created_at from auth.users where id = '…';\n",[21,271,269],{"__ignoreMap":23},[10,273,274],{},"Kommt keine Zeile zurück, gibt es die Person, die dein Profil nennt, in diesem\nProjekt nicht.",[35,276,278],{"id":277},"woher-kommt-eine-zeile-die-ins-leere-zeigt","Woher kommt eine Zeile, die ins Leere zeigt?",[10,280,281],{},"Von einer von wenigen Stellen, und die Tabelle in der Detailzeile sagt dir, wo du\nzuerst suchst.",[10,283,284,287],{},[201,285,286],{},"Die Zeile, auf die sie zeigt, wurde nie gespeichert, oder erst als zweite."," Eine\nBestellung und ihre Positionen sind zwei Speichervorgänge. Speichert deine App\nzuerst die Positionen, oder speichert sie die Bestellung, bekommt einen Fehler, den\nsie nie liest, und macht mit den Positionen weiter, dann nennt jede Position eine\nBestellung, die es nicht gibt. Speichere zuerst die Zeile, auf die gezeigt wird,\nnimm ihre id aus der Antwort, und lies den Fehler jedes Schritts vor dem nächsten:",[14,289,294],{"className":290,"code":292,"language":293,"meta":23},[291],"language-js","const { data: order, error } = await supabase\n  .from('orders')\n  .insert({ user_id: user.id })\n  .select('id')\n  .single()\n\nif (error) throw error\n\nconst { error: linesError } = await supabase\n  .from('order_items')\n  .insert(cart.map((item) => ({ ...item, order_id: order.id })))\n","js",[21,295,292],{"__ignoreMap":23},[10,297,298,301,302,307,308,312],{},[21,299,300],{},".select()"," nach einem Insert liest die neue Zeile zurück, also braucht die Person\neine Regel, die ihr erlaubt, ihre eigenen Bestellungen zu lesen. Die\n",[43,303,306],{"href":304,"rel":305},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fsql-createpolicy.html",[47],"Dokumentation von Postgres zu Policies","\nsagt, dass eine neue Zeile, die an den Leseregeln scheitert, das Speichern mit\neinem Fehler abbricht, und in Supabase heißt dieser Fehler\n",[43,309,311],{"href":310},"\u002Fblog\u002Fnew-row-violates-row-level-security-policy","new row violates row-level security policy",".\nDie beiden Speichervorgänge sind getrennte Anfragen, also ist die Bestellung schon\ngespeichert, wenn die Positionen scheitern: Versuch die Positionen noch einmal, oder\nlösch die Bestellung.",[314,315],"diagram",{"alt":316,"caption":317,"src":318},"Zwei Bahnen. In der ersten wird eine Bestellposition an Bestellung 14 geschickt, bevor es diese Bestellung gibt: In der Straße der Bestellungen ist dort, wo 14 stehen sollte, ein leeres Grundstück bernsteinfarben umrandet, und die Bahn endet bei 23503 und 409. In der zweiten wird Bestellung 14 zuerst gespeichert und steht in Türkis erleuchtet, die Position folgt mit ihrer Nummer, die Straße hat das Haus, und die Bahn endet mit einem Haken.","Positionen, die vor ihrer Bestellung gespeichert werden, zeigen auf ein Haus, das noch nicht steht. Speichere die Bestellung, nimm ihre id aus der Antwort, dann speichere die Positionen.","\u002Fblog\u002Fviolates-foreign-key-constraint\u002Fsave-the-order-first-1600x760.png",[10,320,321,324,325,327,328,330,331,333,334,337,338,341],{},[201,322,323],{},"Die id kam von woanders."," Jedes Supabase-Projekt hat sein eigenes ",[21,326,246],{},".\nEine id aus deinem Testprojekt, eine, die ein KI-Builder aus einer Vorschau im Code\nstehen gelassen hat, oder Zeilen, die von einem Projekt in ein anderes kopiert\nwurden, nennen alle Personen, die das Live-Projekt nie gesehen hat. Wenn du Daten\nzwischen Projekten kopierst, kopier die Zeilen, auf die gezeigt wird, vor den\nZeilen, die auf sie zeigen. Die Personen hinter einer Spalte, die auf ",[21,329,246],{},"\nzeigt, lassen sich so nicht kopieren: Sie registrieren sich im neuen Projekt und\nbekommen dabei neue ids. Ein Profil, dessen ",[21,332,29],{}," die id der Person sein muss, geht\nauch kaputt, wenn die App sie mit einer frischen Zufallszahl füllt, etwa\n",[21,335,336],{},"crypto.randomUUID()",". Diese Nummer gehört zu keinem Haus. Nimm ",[21,339,340],{},"user.id"," aus der\nangemeldeten Sitzung.",[10,343,344,347,348,351],{},[201,345,346],{},"Die Zeile wurde zwischendurch gelöscht."," Ein Produkt, das aus deinem Shop\nentfernt wurde, während es in jemandes Warenkorb lag, oder ein Konto, das in einem\nanderen Tab gelöscht wurde. Der Speichervorgang danach zeigt auf ein Haus, das\nstand, als die Seite geladen wurde. Die Reparatur liegt in deiner App: Prüf auf\n",[21,349,350],{},"error.code === '23503'"," und sag der Person, was sich geändert hat, etwa \"Dieses\nProdukt ist nicht mehr verfügbar.\"",[10,353,354,357,358,360,361,364,365,370,371,374,375,378,379,383,384,387],{},[201,355,356],{},"Ein Trigger, der zu früh läuft."," Ein Trigger auf ",[21,359,246],{},", der jede neue\nPerson in ",[21,362,363],{},"profiles"," kopiert, muss laufen, nachdem die Person gespeichert ist. Die\n",[43,366,369],{"href":367,"rel":368},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Ftrigger-datachanges.html",[47],"Dokumentation von Postgres zu Triggern","\nsagt, die Änderung, die einen zeilenweisen ",[21,372,373],{},"BEFORE","-Trigger auslöst, \"is naturally\nnot visible to SQL commands executed in a row-level BEFORE trigger, because it\nhasn't happened yet\". Ein Profil, das aus einem Trigger ",[21,376,377],{},"before insert"," geschrieben\nwird, zeigt also auf eine Person, die es noch nicht gibt. Jede Registrierung\nscheitert, und Supabase meldet sie als\n",[43,380,382],{"href":381},"\u002Fblog\u002Fdatabase-error-saving-new-user","\"Database error saving new user\"",". Das\nMuster von Supabase selbst, das dieser Beitrag vollständig zeigt, nutzt\n",[21,385,386],{},"after insert",".",[35,389,391],{"id":390},"warum-kommt-ein-leerer-wert-durch","Warum kommt ein leerer Wert durch?",[10,393,394],{},"Weil ein Fremdschlüssel nur Werte prüft, die da sind. Die Dokumentation von\nPostgres zu Constraints sagt es so: \"Normally, a referencing row need not satisfy\nthe foreign key constraint if any of its referencing columns are null.\" Null ist\ndas Wort von Postgres für gar keinen Wert. Ein Brief ohne Hausnummer wird nie\nnachgeschlagen. Er landet im Sack und gehört zu keinem Haus.",[10,396,397,398,401],{},"Eine Bestellposition, die mit leerem ",[21,399,400],{},"order_id"," gespeichert wird, wird also\nangenommen und gehört zu keiner Bestellung. Deine App trifft später auf sie, als\nPosition, die in niemandes Warenkorb auftaucht. Dieselbe Seite nennt die Reparatur:\n\"declare the referencing column(s) as NOT NULL\".",[14,403,406],{"className":404,"code":405,"language":256,"meta":23},[254],"alter table public.order_items alter column order_id set not null;\n",[21,407,405],{"__ignoreMap":23},[10,409,410,411,415,416,418,419,387],{},"Danach wird auch ein leerer Wert abgelehnt, mit einer Meldung, die die Spalte\ndirekt nennt,\n",[43,412,414],{"href":413},"\u002Fblog\u002Fnull-value-violates-not-null-constraint","null value in column violates not-null constraint",".\nDer Befehl scheitert, solange es schon Zeilen mit leerem ",[21,417,400],{}," gibt, also\nliste sie zuerst auf mit ",[21,420,421],{},"select * from public.order_items where order_id is null",[35,423,425],{"id":424},"kann-eine-regel-die-gesuchte-zeile-verstecken","Kann eine Regel die gesuchte Zeile verstecken?",[10,427,428,429,434,435,439],{},"Nein. Postgres prüft einen Fremdschlüssel gegen jede Zeile der anderen Tabelle,\negal was deine Row-Level-Security-Regeln sagen.\n",[43,430,433],{"href":431,"rel":432},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fddl-rowsecurity.html",[47],"Seine Dokumentation zu Row Security","\nsagt das direkt: \"Referential integrity checks, such as unique or primary key\nconstraints and foreign key references, always bypass row security to ensure that\ndata integrity is maintained.\" In\n",[43,436,438],{"href":175,"rel":437},[47],"dem Code, der die Prüfung ausführt",",\nwechselt die Suche zum Eigentümer der anderen Tabelle, mit einem Flag, das selbst\neine erzwungene Regel nicht greifen lässt.",[10,441,442,443,445],{},"Das Briefzentrum arbeitet also mit dem vollständigen Straßenplan. Deine App sieht\ndie Straße durch ihre Regeln, die ihr vielleicht nur die Häuser zeigen, die sie\nbesuchen darf, und die Prüfung nutzt diese Sicht nie. Ein ",[21,444,86],{}," heißt, dass das\nHaus für niemanden da ist. Gibt es die Zeile und deine App kann sie nicht sehen,\ngeht das Speichern durch.",[314,447],{"alt":448,"caption":449,"src":450},"Dieselbe Straße mit Häusern, zweimal gezeichnet. Links, aus einem Browserfenster durch einen Zaun mit Vorhängeschloss gesehen, ist nur Haus 12 erleuchtet, die anderen sind blasse gestrichelte Umrisse. Rechts geht ein Brief an 14, eines der blassen Häuser, hinunter in dieselbe Straße, in der jedes Haus fest gezeichnet ist, und er bekommt einen Haken.","Deine App liest die Zeilen, die ihre Regeln erlauben. Die Fremdschlüssel-Prüfung liest jede Zeile, also wird eine Zeile angenommen, die auf ein Haus zeigt, das deine App nicht sehen kann.","\u002Fblog\u002Fviolates-foreign-key-constraint\u002Fthe-check-sees-every-house-1600x760.png",[35,452,454],{"id":453},"kann-jemand-eine-zeile-an-einen-fremden-datensatz-hängen","Kann jemand eine Zeile an einen fremden Datensatz hängen?",[10,456,457],{},"Ja, außer deine Insert-Regel prüft, wem dieser Datensatz gehört. Die Prüfung, ob\ndas Haus existiert, übergeht deine Regeln, also sagt sie Ja zu einem Haus, das\njemand anderem gehört.",[10,459,460,461,464],{},"Nimm ",[21,462,463],{},"order_items"," mit dieser Insert-Regel, die sicher aussieht:",[14,466,469],{"className":467,"code":468,"language":256,"meta":23},[254],"create policy \"Add your own order lines\"\non public.order_items for insert\nto authenticated\nwith check (auth.uid() = user_id);\n",[21,470,468],{"__ignoreMap":23},[10,472,473,474,477,478,480],{},"Sie prüft, dass die Position dir gehört, und schaut nie, zu wessen Bestellung sie\nkommt. Eine angemeldete Person, die ihre eigene ",[21,475,476],{},"user_id"," mit der ",[21,479,400],{}," von\njemand anderem schickt, besteht die Regel und den Fremdschlüssel, und eine Position,\ndie sie geschrieben hat, steht jetzt auf der Bestellung einer fremden Person.\nDieselbe Form setzt einen Kommentar unter den privaten Beitrag einer anderen Person\noder ein Mitglied in ein Team, in das es niemand eingeladen hat.",[10,482,483],{},"Ersetze sie durch eine Regel, die sich die Bestellung ansieht:",[14,485,488],{"className":486,"code":487,"language":256,"meta":23},[254],"create policy \"Add lines to your own orders\"\non public.order_items for insert\nto authenticated\nwith check (\n  exists (\n    select 1 from public.orders\n    where orders.id = order_items.order_id\n      and orders.user_id = auth.uid()\n  )\n);\n",[21,489,487],{"__ignoreMap":23},[10,491,492,493,497],{},"Lösch die erste Regel, wenn du diese hinzufügst. Postgres verknüpft Regeln\nderselben Art mit \"OR\", so steht es in\n",[43,494,496],{"href":304,"rel":495},[47],"seiner Dokumentation zu Policies",",\nalso würde die alte die Position allein weiter durchlassen.",[10,499,500,501,503],{},"Die Prüfung beantwortet außerdem eine Frage für jeden, der einfügen darf. Der Satz\nnach dem oben zitierten, in der Dokumentation zu Row Security, warnt davor: \"Care\nmust be taken when developing schemas and row level policies to avoid 'covert\nchannel' leaks of information through such referential integrity checks.\" Ein\nSpeichern, das mit ",[21,502,86],{}," scheitert, sagt, dass ein Haus nicht da ist. Eines, das\ngelingt, sagt, dass es da ist, in einer Straße, die die Person nicht lesen kann.\nDie Nutzer-ids von Supabase sind lange Zufallswerte, die niemand errät. Zeigt eine\nSpalte auf etwas Erratbares, etwa eine E-Mail-Adresse oder eine id, die ab 1\nhochzählt, kann eine Person Werte einzeln ausprobieren und erfahren, welche es gibt.",[35,505,507],{"id":506},"solltest-du-den-fremdschlüssel-entfernen","Solltest du den Fremdschlüssel entfernen?",[10,509,510,511,513],{},"Nein. Ohne ihn geht das Speichern durch, die Zeile zeigt ins Leere, und ab dann\nsagt dir nichts mehr, wann das passiert. Ein Warenkorb zeigt eine Position für ein\nProdukt, das du letzten Monat entfernt hast, und ein Profil bleibt für eine Person\nzurück, die nicht mehr in ",[21,512,246],{}," steht.",[10,515,516,517,520,521,523,524,526,527,530],{},"Die Regel entscheidet auch, was mit diesen Zeilen passiert, wenn die Zeile gelöscht\nwird, auf die sie zeigen. Mit ",[21,518,519],{},"on delete cascade"," auf der Regel von ",[21,522,363],{}," zu\n",[21,525,246],{}," löscht das Löschen einer Person auch ihr Profil. Ohne die Regel bleibt\ndas Profil, mit allen persönlichen Angaben, die es enthält.\n",[43,528,529],{"href":188},"Der Beitrag über das Löschen von Nutzern","\nzeigt, wie du festlegst, was jede Tabelle behält.",[10,532,533],{},"Zeigen alte Zeilen schon ins Leere, liste sie auf, bevor du eine Regel wieder\nhinzufügst oder verschärfst, denn Postgres weigert sich, einen Fremdschlüssel\nanzulegen, solange eine Zeile ihn verletzt:",[14,535,538],{"className":536,"code":537,"language":256,"meta":23},[254],"select i.*\nfrom public.order_items i\nleft join public.orders o on o.id = i.order_id\nwhere i.order_id is not null\n  and o.id is null;\n",[21,539,537],{"__ignoreMap":23},[10,541,542],{},"Entscheide für jede einzelne, ob sie ihre Bestellung zurückbekommt oder geht. Das\nLöschen lässt sich ohne eine Kopie der Datenbank von vorher nicht rückgängig\nmachen.",[35,544,546],{"id":545},"bevor-du-die-zeilen-reparierst","Bevor du die Zeilen reparierst",[10,548,549],{},"Behalte eine Kopie der Datenbank von vor dem Moment, in dem du Zeilen löschst, die\nins Leere zeigen, Daten zwischen Projekten kopierst oder eine Regel änderst, und\nerfahre es, wenn eine Tabelle anfängt, Fremden zu antworten.",[10,551,552],{},[201,553,554,558],{},[43,555,557],{"href":556},"\u002Fsupabase-backups","Reeve Care"," bewahrt eine Kopie deiner Supabase-Datenbank auf:",[560,561,562,566,569,572,575,578],"ul",{},[563,564,565],"li",{},"eine verschlüsselte Kopie nach Zeitplan, auf Reeves eigenem Speicher in der EU, außerhalb deines Supabase-Kontos",[563,567,568],{},"jede Zeile, wie sie war, bevor eine Aufräumaktion die falschen gelöscht hat",[563,570,571],{},"jede Kopie wird geprüft, bevor sie zählt, indem die Zeilen in jeder Tabelle gezählt werden",[563,573,574],{},"auch deine hochgeladenen Dateien, sobald du eine Storage-Zugangsberechtigung verbindest",[563,576,577],{},"Wiederherstellung mit einem Klick, vorher wird eine Sicherheitskopie des aktuellen Stands angelegt",[563,579,580],{},"alles, was Monitor macht",[10,582,583],{},[201,584,585,589],{},[43,586,588],{"href":587},"\u002Fwebsite-monitoring","Reeve Monitor"," prüft weiter von außen:",[560,591,592,595,598,601],{},[563,593,594],{},"ob deine App erreichbar ist, jede Minute geprüft",[563,596,597],{},"der vollständige Sicherheitscheck jede Stunde, für bis zu drei Apps",[563,599,600],{},"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",[563,602,603],{},"ein monatlicher Bericht in klarer Sprache über das, was er gesehen hat",[35,605,607],{"id":606},"was-du-jetzt-tun-solltest","Was du jetzt tun solltest",[609,610,611],"key-takeaways",{},[560,612,613,621,624,627,634,637],{},[563,614,615,616,618,619,387],{},"Lies die Tabelle in der Detailzeile. In Supabase heißt ",[21,617,242],{}," dort fast immer ",[21,620,246],{},[563,622,623],{},"Finde den Wert: in der Detailzeile oder in der Anfrage, die deine App geschickt hat.",[563,625,626],{},"Speichere zuerst die Zeile, auf die gezeigt wird, nimm ihre id aus der Antwort, und lies jeden Fehler vor dem nächsten Speichern.",[563,628,629,630,633],{},"Gib einer Spalte, die auf etwas zeigen muss, ",[21,631,632],{},"not null",", damit auch ein leerer Wert abgelehnt wird.",[563,635,636],{},"Lass jede Insert-Regel prüfen, wem die Zeile gehört, auf die die neue zeigt.",[563,638,639],{},"Behalte den Fremdschlüssel, und liste die Zeilen auf, die ins Leere zeigen, bevor du eine davon löschst.",[10,641,642,643,646],{},"Fang bei der Tabelle in der Detailzeile und der id an, die deine App geschickt hat.\nLöscht die Reparatur Zeilen oder kopiert sie zwischen Projekten,\n",[43,644,645],{"href":556},"behalte eine Kopie der Datenbank"," von vor dem Ausführen.",{"title":23,"searchDepth":648,"depth":648,"links":649},3,[650,652,653,654,655,656,657,658,659],{"id":37,"depth":651,"text":38},2,{"id":193,"depth":651,"text":194},{"id":277,"depth":651,"text":278},{"id":390,"depth":651,"text":391},{"id":424,"depth":651,"text":425},{"id":453,"depth":651,"text":454},{"id":506,"depth":651,"text":507},{"id":545,"depth":651,"text":546},{"id":606,"depth":651,"text":607},"Sicherheitsgrundlagen","\u002Fblog\u002Fviolates-foreign-key-constraint\u002Fcover-1200x630.png","Eine Straße mit nummerierten Häusern, ein Grundstück leer und bernsteinfarben umrandet, davor ein Brief an die fehlende Nummer.","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.",false,"md",[667,670,673,676,679],{"q":668,"a":669},"Was bedeutet insert or update on table violates foreign key constraint?","Die Zeile, die du speichern wolltest, zeigt auf eine Zeile in einer anderen Tabelle, die es nicht gibt, und Postgres hat sie abgelehnt. Geschrieben wurde nichts. Die Meldung nennt die Tabelle, in die du geschrieben hast, und die Regel, die Detailzeile die Tabelle, in der Postgres gesucht hat. Der Code ist 23503, und aus einer Supabase-App kommt er als HTTP 409.",{"q":671,"a":672},"Warum nennt der Fehler die Tabelle \"users\", obwohl ich keine habe?","Weil fast sicher auth.users gemeint ist, die Tabelle, in der Supabase Auth alle registrierten Personen führt. Postgres gibt den Namen einer Tabelle ohne ihr Schema aus. Deine Spalte zeigt auf eine Person, und die id darin gehört niemandem in diesem Projekt.",{"q":674,"a":675},"Kann Row Level Security einen Fremdschlüssel-Fehler auslösen?","Nein. Postgres prüft einen Fremdschlüssel gegen jede Zeile der anderen Tabelle, auch gegen Zeilen, die deine Regeln verbergen. Der Fehler heißt also, dass die Zeile wirklich fehlt. Dieselbe Tatsache wirkt auch andersherum: Eine Zeile, die auf einen Datensatz zeigt, den die Person nicht sehen kann, wird angenommen, außer deine Insert-Regel prüft, wem dieser Datensatz gehört.",{"q":677,"a":678},"Warum sagt die Detailzeile \"Key is not present\" ohne den Wert?","Postgres lässt den Wert weg, wenn Row Level Security auf der Tabelle eingeschaltet ist, in die du geschrieben hast, oder wenn der Aufrufer diese Spalte nicht lesen darf. Der Wert ist der, den deine App geschickt hat, also lies ihn aus der Anfrage im Netzwerk-Tab der Entwicklertools deines Browsers.",{"q":680,"a":681},"Soll ich den Fremdschlüssel entfernen, damit der Fehler aufhört?","Nein. Die Speichervorgänge gehen durch, und die Zeilen zeigen ins Leere: eine Bestellposition ohne Bestellung, ein Profil ohne Person dahinter. Die Regel entscheidet auch, was mit diesen Zeilen passiert, wenn ein Nutzer gelöscht wird. Finde heraus, welche Zeile fehlt, und speichere sie zuerst, oder korrigiere die id.","\u002Fblog\u002Fviolates-foreign-key-constraint\u002Fcard-800x500.png",[684,685,686,687,688,689],"violates foreign key constraint supabase","insert or update on table violates foreign key constraint","key is not present in table","postgres 23503","supabase fremdschlüssel auth.users","supabase fremdschlüssel row level security",{},true,"\u002Fblog\u002Fviolates-foreign-key-constraint","2026-11-07",{"title":5,"description":663},"blog\u002Fviolates-foreign-key-constraint",[697,698,699],"Supabase \"violates foreign key constraint\" beim Einfügen (Postgres-Code 23503, HTTP 409 in deiner App) heißt: Die Zeile, die du gespeichert hast, nennt eine Zeile in einer anderen Tabelle, die es nicht gibt. Gespeichert wurde nichts. Die Meldung nennt deine Tabelle und die Regel, die Detailzeile die Tabelle, in der Postgres gesucht hat.","Die fehlende Zeile wurde nie gespeichert, erst als zweite gespeichert, zwischendurch gelöscht, oder sie liegt in einem anderen Projekt. Steht in der Detailzeile \"users\", ist fast immer auth.users gemeint, wo Supabase Auth alle registrierten Personen führt.","Die Prüfung liest jede Zeile der anderen Tabelle, egal was deine Row-Level-Security-Regeln sagen. Sie nimmt also auch eine Zeile an, die auf den Datensatz einer anderen Person zeigt, und deine Insert-Regel muss prüfen, wem dieser Datensatz gehört.","Xc72zBKl2Criz7i_xVFogMQ9kiOMBv5yr8FIdu0mFpI",[702,708,714,715,720,726,732,738,744,750,756,762,768,774,780,786,792,798,804,810,816,822,828,834,840,846,852,857,863,869,875,881,887,893,899,905,911,917,923,929,935,941,947,953,959,965,970,976,982,988,994,1000,1006,1012,1018,1024,1030,1036,1042,1048,1054,1060,1065,1071,1077,1083,1089,1095,1101,1107,1113,1119,1125,1131,1137,1143,1148,1154,1160,1166,1172,1178,1184,1190,1195,1201,1207,1213,1218,1224],{"path":703,"title":704,"description":705,"published":706,"category":660,"image":707,"draft":664},"\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":709,"title":710,"description":711,"published":712,"category":660,"image":713,"draft":664},"\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":692,"title":5,"description":663,"published":693,"category":660,"image":682,"draft":664},{"path":413,"title":716,"description":717,"published":718,"category":660,"image":719,"draft":664},"Supabase-Fehler \"null value in column violates not-null constraint\"","Supabase \"null value in column violates not-null constraint\": Eine Pflichtspalte kam leer an. Woher die Null kommt, und die Lösung.","2026-11-06","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fcard-800x500.png",{"path":721,"title":722,"description":723,"published":724,"category":660,"image":725,"draft":664},"\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":727,"title":728,"description":729,"published":730,"category":660,"image":731,"draft":664},"\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":733,"title":734,"description":735,"published":736,"category":660,"image":737,"draft":664},"\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":739,"title":740,"description":741,"published":742,"category":660,"image":743,"draft":664},"\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":745,"title":746,"description":747,"published":748,"category":660,"image":749,"draft":664},"\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":751,"title":752,"description":753,"published":754,"category":660,"image":755,"draft":664},"\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":188,"title":757,"description":758,"published":759,"category":760,"image":761,"draft":664},"\"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":763,"title":764,"description":765,"published":766,"category":660,"image":767,"draft":664},"\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":769,"title":770,"description":771,"published":772,"category":660,"image":773,"draft":664},"\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":775,"title":776,"description":777,"published":778,"category":660,"image":779,"draft":664},"\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":781,"title":782,"description":783,"published":784,"category":660,"image":785,"draft":664},"\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":787,"title":788,"description":789,"published":790,"category":660,"image":791,"draft":664},"\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":793,"title":794,"description":795,"published":796,"category":660,"image":797,"draft":664},"\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint","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":799,"title":800,"description":801,"published":802,"category":660,"image":803,"draft":664},"\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":805,"title":806,"description":807,"published":808,"category":660,"image":809,"draft":664},"\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":811,"title":812,"description":813,"published":814,"category":660,"image":815,"draft":664},"\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\" 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":817,"title":818,"description":819,"published":820,"category":660,"image":821,"draft":664},"\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":823,"title":824,"description":825,"published":826,"category":660,"image":827,"draft":664},"\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":829,"title":830,"description":831,"published":832,"category":660,"image":833,"draft":664},"\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":835,"title":836,"description":837,"published":838,"category":660,"image":839,"draft":664},"\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":841,"title":842,"description":843,"published":844,"category":660,"image":845,"draft":664},"\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":847,"title":848,"description":849,"published":850,"category":660,"image":851,"draft":664},"\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":381,"title":853,"description":854,"published":855,"category":660,"image":856,"draft":664},"\"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":858,"title":859,"description":860,"published":861,"category":660,"image":862,"draft":664},"\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":864,"title":865,"description":866,"published":867,"category":660,"image":868,"draft":664},"\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":870,"title":871,"description":872,"published":873,"category":660,"image":874,"draft":664},"\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":876,"title":877,"description":878,"published":879,"category":660,"image":880,"draft":664},"\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":882,"title":883,"description":884,"published":885,"category":660,"image":886,"draft":664},"\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":888,"title":889,"description":890,"published":891,"category":660,"image":892,"draft":664},"\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":894,"title":895,"description":896,"published":897,"category":660,"image":898,"draft":664},"\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":900,"title":901,"description":902,"published":903,"category":660,"image":904,"draft":664},"\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":906,"title":907,"description":908,"published":909,"category":660,"image":910,"draft":664},"\u002Fblog\u002Fmove-a-secret-to-an-edge-function","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":912,"title":913,"description":914,"published":915,"category":660,"image":916,"draft":664},"\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":918,"title":919,"description":920,"published":921,"category":660,"image":922,"draft":664},"\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":924,"title":925,"description":926,"published":927,"category":660,"image":928,"draft":664},"\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":930,"title":931,"description":932,"published":933,"category":660,"image":934,"draft":664},"\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":936,"title":937,"description":938,"published":939,"category":660,"image":940,"draft":664},"\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":942,"title":943,"description":944,"published":945,"category":660,"image":946,"draft":664},"\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":948,"title":949,"description":950,"published":951,"category":760,"image":952,"draft":664},"\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":954,"title":955,"description":956,"published":957,"category":760,"image":958,"draft":664},"\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":960,"title":961,"description":962,"published":963,"category":760,"image":964,"draft":664},"\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":966,"title":967,"description":968,"published":963,"category":660,"image":969,"draft":664},"\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":971,"title":972,"description":973,"published":974,"category":660,"image":975,"draft":664},"\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":977,"title":978,"description":979,"published":980,"category":660,"image":981,"draft":664},"\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":983,"title":984,"description":985,"published":986,"category":760,"image":987,"draft":664},"\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":989,"title":990,"description":991,"published":992,"category":660,"image":993,"draft":664},"\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":995,"title":996,"description":997,"published":998,"category":660,"image":999,"draft":664},"\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":1001,"title":1002,"description":1003,"published":1004,"category":660,"image":1005,"draft":664},"\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":1007,"title":1008,"description":1009,"published":1010,"category":660,"image":1011,"draft":664},"\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":1013,"title":1014,"description":1015,"published":1016,"category":660,"image":1017,"draft":664},"\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":1019,"title":1020,"description":1021,"published":1022,"category":660,"image":1023,"draft":664},"\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":1025,"title":1026,"description":1027,"published":1028,"category":660,"image":1029,"draft":664},"\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":1031,"title":1032,"description":1033,"published":1034,"category":660,"image":1035,"draft":664},"\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":1037,"title":1038,"description":1039,"published":1040,"category":760,"image":1041,"draft":664},"\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":1043,"title":1044,"description":1045,"published":1046,"category":760,"image":1047,"draft":664},"\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":1049,"title":1050,"description":1051,"published":1052,"category":760,"image":1053,"draft":664},"\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":1055,"title":1056,"description":1057,"published":1058,"category":660,"image":1059,"draft":664},"\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":227,"title":1061,"description":1062,"published":1063,"category":660,"image":1064,"draft":664},"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":1066,"title":1067,"description":1068,"published":1069,"category":660,"image":1070,"draft":664},"\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":1072,"title":1073,"description":1074,"published":1075,"category":660,"image":1076,"draft":664},"\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":1078,"title":1079,"description":1080,"published":1081,"category":660,"image":1082,"draft":664},"\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":1084,"title":1085,"description":1086,"published":1087,"category":660,"image":1088,"draft":664},"\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":1090,"title":1091,"description":1092,"published":1093,"category":660,"image":1094,"draft":664},"\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":1096,"title":1097,"description":1098,"published":1099,"category":660,"image":1100,"draft":664},"\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":1102,"title":1103,"description":1104,"published":1105,"category":660,"image":1106,"draft":664},"\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":1108,"title":1109,"description":1110,"published":1111,"category":660,"image":1112,"draft":664},"\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":1114,"title":1115,"description":1116,"published":1117,"category":760,"image":1118,"draft":664},"\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":1120,"title":1121,"description":1122,"published":1123,"category":660,"image":1124,"draft":664},"\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":1126,"title":1127,"description":1128,"published":1129,"category":760,"image":1130,"draft":664},"\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":1132,"title":1133,"description":1134,"published":1135,"category":660,"image":1136,"draft":664},"\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":1138,"title":1139,"description":1140,"published":1141,"category":660,"image":1142,"draft":664},"\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":310,"title":1144,"description":1145,"published":1146,"category":660,"image":1147,"draft":664},"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":1149,"title":1150,"description":1151,"published":1152,"category":660,"image":1153,"draft":664},"\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":1155,"title":1156,"description":1157,"published":1158,"category":760,"image":1159,"draft":664},"\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":1161,"title":1162,"description":1163,"published":1164,"category":760,"image":1165,"draft":664},"\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":1167,"title":1168,"description":1169,"published":1170,"category":660,"image":1171,"draft":664},"\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":1173,"title":1174,"description":1175,"published":1176,"category":660,"image":1177,"draft":664},"\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":1179,"title":1180,"description":1181,"published":1182,"category":760,"image":1183,"draft":664},"\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":1185,"title":1186,"description":1187,"published":1188,"category":660,"image":1189,"draft":664},"\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":1191,"title":1192,"description":1193,"published":1188,"category":660,"image":1194,"draft":664},"\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":1196,"title":1197,"description":1198,"published":1199,"category":660,"image":1200,"draft":664},"\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":1202,"title":1203,"description":1204,"published":1205,"category":760,"image":1206,"draft":664},"\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":1208,"title":1209,"description":1210,"published":1211,"category":660,"image":1212,"draft":664},"\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":1214,"title":1215,"description":1216,"published":1211,"category":760,"image":1217,"draft":664},"\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":1219,"title":1220,"description":1221,"published":1222,"category":760,"image":1223,"draft":664},"\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":1225,"title":1226,"description":1227,"published":1222,"category":660,"image":1228,"draft":664},"\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",1794213638635]