[{"data":1,"prerenderedAt":1335},["ShallowReactive",2],{"blog-uk-null-value-violates-not-null-constraint":3,"blog-index-uk":809},{"id":4,"title":5,"body":6,"category":768,"cover":769,"coverAlt":770,"description":771,"draft":772,"extension":773,"faq":774,"image":789,"keywords":790,"meta":798,"navigation":799,"ogTitle":5,"path":800,"published":801,"seo":802,"stem":803,"tldr":804,"updated":801,"__hash__":808},"blog_uk\u002Fblog\u002Fnull-value-violates-not-null-constraint.md","Помилка Supabase \"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",{},"Ви натискаєте Зберегти на новому завданні у своєму застосунку, і нічого не\nзберігається. Сторінка не показує помилки, список лишається, як був. Консоль\nбраузера знає причину:",[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\" означає, що рядок\nдійшов до вашої бази даних без нічого в стовпці, який завжди мусить мати\nзначення, і Postgres відхилив увесь рядок. Тут це стовпець ",[21,28,29],{},"category_id"," у вашій\nтаблиці ",[21,32,33],{},"tasks",". Під час збереження категорію не надіслали.",[10,36,37],{},"Найшвидше виправлення полягає в тому, щоб прибрати правило: один рядок SQL, і\nстовпець може лишатися порожнім. Помилка зникає, а відтоді приходять рядки з\nпорожнім полем, на які решта застосунку натрапляє там, куди ви не дивилися.\nСпершу зʼясуйте, звідки взялося порожнє значення. Майже завжди це одне з кількох\nмісць.",[39,40,42],"h2",{"id":41},"що-означає-null-value-in-column-violates-not-null-constraint","Що означає \"null value in column violates not-null constraint\"?",[10,44,45],{},"Це означає, що один стовпець рядка, який ваш застосунок хотів зберегти, був\nпорожнім, що цей стовпець ніколи не може бути порожнім, і що Postgres відмовився\nзберігати будь-яку частину рядка. Нічого не записано. Null у Postgres означає\nвідсутність значення, і це не нуль і не текст без жодної літери.",[10,47,48],{},"Уявіть кожен рядок як паперовий бланк, а таблицю як шухляду картотеки, куди він\nлягає. Обмеження not-null схоже на зірочку, надруковану біля поля. Postgres сидить\nбіля шухляди й не підшиває жодного бланка, де поле із зірочкою порожнє. Бланк\nповертається, і повідомлення каже, яке саме поле.",[50,51,52,68],"table",{},[53,54,55],"thead",{},[56,57,58,62,65],"tr",{},[59,60,61],"th",{},"Поле",[59,63,64],{},"Приклад",[59,66,67],{},"Що воно каже",[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,93,94],{},"Код Postgres для цієї помилки, у ",[87,88,92],"a",{"href":89,"rel":90},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Futils\u002Ferrcodes.txt",[91],"nofollow","його списку кодів"," він має назву ",[21,95,96],{},"not_null_violation",[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],{},"Порожній стовпець, потім таблиця. \"Relation\" у Postgres означає таблицю",[56,113,114,119,128],{},[74,115,116],{},[21,117,118],{},"details",[74,120,121,124,125],{},[21,122,123],{},"Failing row contains (…)"," або ",[21,126,127],{},"null",[74,129,130],{},"Копія відхиленого рядка, коли Postgres вирішує, що той, хто викликає, може її бачити. Розділ нижче пояснює, коли саме",[56,132,133,138,142],{},[74,134,135],{},[21,136,137],{},"hint",[74,139,140],{},[21,141,127],{},[74,143,144],{},"Для цієї помилки нічого додати",[56,146,147,150,155],{},[74,148,149],{},"Статус HTTP",[74,151,152],{},[21,153,154],{},"400 Bad Request",[74,156,157,158,161,162,164,165,170],{},"PostgREST, частина Supabase, яка перетворює виклики ",[21,159,160],{},"supabase.from(...)"," на SQL, не має окремого випадку для ",[21,163,82],{},", тож ",[87,166,169],{"href":167,"rel":168},"https:\u002F\u002Fgithub.com\u002FPostgREST\u002Fpostgrest\u002Fblob\u002Fmain\u002Fsrc\u002Flibrary\u002FPostgREST\u002FError.hs",[91],"його код"," повертає 400",[10,172,173,174,179],{},"Формулювання належить самому Postgres, воно з\n",[87,175,178],{"href":176,"rel":177},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Fexecutor\u002FexecMain.c",[91],"функції, яка породжує помилку",",\nі читається однаково, звідки б не прийшов рядок: із вашого застосунку, з тригера\nчи з SQL Editor.",[39,181,183],{"id":182},"звідки-взялося-порожнє-значення","Звідки взялося порожнє значення?",[10,185,186],{},"Зазвичай з одного з трьох місць у коді вашого власного застосунку, і стовпець у\nповідомленні підказує, куди дивитися спершу. Ще два починаються всередині бази\nданих.",[10,188,189,193,194,196,197,200],{},[190,191,192],"strong",{},"Поле, яке ваш застосунок ніколи не надсилає."," Це найчастіший випадок. Стовпець\nстав обовʼязковим уже після того, як форму зробили: конструктор зі ШІ додав\n",[21,195,29],{}," до таблиці, коли ви попросили категорії, позначив його\nобовʼязковим і залишив без змін форму, що зберігає завдання. Ніщо в запиті не\nназиває стовпець, значення за замовчуванням у стовпця немає, тож він порожній.\nПорівняйте стовпці у своєму Table Editor з обʼєктом, який ваш застосунок передає в\n",[21,198,199],{},".insert()",". Відсутній якраз той, що в повідомленні.",[10,202,203,204,207,208,210,211,215],{},"Ключ, записаний інакше, ніж його стовпець, сюди навіть не доходить. PostgREST\nвідхиляє ",[21,205,206],{},"categoryId",", надісланий у стовпець ",[21,209,29],{},", раніше, ніж Postgres\nпобачить рядок, і\n",[87,212,214],{"href":213},"\u002Fblog\u002Fsupabase-schema-cache-column-error","ця помилка має окремий допис",".",[10,217,218,221,222,225,226,229,230,237,238,240],{},[190,219,220],{},"Null, який ваш код надсилає навмисно."," ",[21,223,224],{},"{ category_id: selected?.id ?? null }","\nнадсилає явний null, коли нічого не вибрано, а явний null для запиту є значенням.\n",[21,227,228],{},"undefined"," у JavaScript поводиться інакше. У JSON такого значення немає, тож\n",[87,231,234],{"href":232,"rel":233},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FJavaScript\u002FReference\u002FGlobal_Objects\u002FJSON\u002Fstringify",[91],[21,235,236],{},"JSON.stringify","\nлишає його \"omitted (when found in an object)\", і ключ зі значенням ",[21,239,228],{},"\nдо Supabase не доходить узагалі. Від того, що з двох створює ваш код, залежить, чи\nзможе спрацювати значення за замовчуванням, і про це наступний розділ.",[10,242,243,221,246,249,250,253,254,257],{},[190,244,245],{},"Стовпець без значення за замовчуванням, на яке ви розраховували.",[21,247,248],{},"created_at","\nзазвичай заповнюється сам, бо таблиця каже ",[21,251,252],{},"default now()",". ",[21,255,256],{},"user_id"," зазвичай\nні, бо ніщо в Postgres не знає, хто увійшов, поки стовпцю не скажуть, як це\nдізнатися. Розділ далі показує як.",[10,259,260,263,264,267,268,271,272,277,278,281,282,284,285,289,290,292],{},[190,261,262],{},"Реєстрація, під іншим повідомленням."," Тригер, який копіює кожного нового\nкористувача в таблицю ",[21,265,266],{},"profiles",", читає його реєстраційні дані через ",[21,269,270],{},"->>",", а\n",[87,273,276],{"href":274,"rel":275},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Ffunctions-json.html",[91],"JSON-оператори 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\". Форма реєстрації, яка ніколи не надсилала ",[21,279,280],{},"full_name",", дає тригеру\nпорожнє імʼя, обовʼязковий стовпець ",[21,283,280],{}," його відхиляє, і Supabase\nповідомляє про всю реєстрацію як\n",[87,286,288],{"href":287},"\u002Fblog\u002Fdatabase-error-saving-new-user","\"Database error saving new user\"",". Сам\n",[21,291,82],{}," лежить у логах Postgres, і той допис показує, де саме.",[10,294,295,298,299,302,303,307],{},[190,296,297],{},"Видалення деінде."," Зовнішній ключ із ",[21,300,301],{},"on delete set null"," очищує стовпець у\nкожному рядку, що вказував на видалений. Якщо цей стовпець ще й обовʼязковий, з\nцим повідомленням падає саме видалення, і\n",[87,304,306],{"href":305},"\u002Fblog\u002Fdatabase-error-deleting-user","допис про видалення користувачів"," пояснює,\nяк вибрати відповідне правило.",[39,309,311],{"id":310},"але-ж-у-стовпця-є-значення-за-замовчуванням","\"Але ж у стовпця є значення за замовчуванням\"",[10,313,314,315,320],{},"Тоді запит назвав стовпець і надіслав його порожнім. Postgres підставляє значення\nза замовчуванням лише тоді, коли стовпець повністю пропущено в рядку.\n",[87,316,319],{"href":317,"rel":318},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fddl-default.html",[91],"Його документація про значення за замовчуванням","\nкаже: \"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.\"\nЯвний null вважається вказаним значенням. На бланку поле з надрукованим значенням\nвважається заповненим, доки хтось не напише поверх друку \"немає\".",[10,322,323,324,329,330,333,334,336,337,340,341,346,347,349],{},"supabase-js пише \"немає\" поверх в одному несподіваному місці: у пакетній вставці,\nколи ви передаєте масив рядків. Він називає кожен стовпець, що зʼявляється бодай\nв одному з рядків, а\n",[87,325,328],{"href":326,"rel":327},"https:\u002F\u002Fgithub.com\u002Fsupabase\u002Fsupabase-js\u002Fblob\u002Fmaster\u002Fpackages\u002Fcore\u002Fpostgrest-js\u002Fsrc\u002FPostgrestQueryBuilder.ts",[91],"його вихідний код","\nописує параметр, який вирішує долю пропусків, ",[21,331,332],{},"defaultToNull",": \"Make missing\nfields default to ",[21,335,127],{},". Otherwise, use the default value for the column. Only\napplies for bulk inserts.\" Він дорівнює ",[21,338,339],{},"true",", поки ви його не зміните.\n",[87,342,345],{"href":343,"rel":344},"https:\u002F\u002Fdocs.postgrest.org\u002Fen\u002Fstable\u002Freferences\u002Fapi\u002Fpreferences.html",[91],"Документація PostgREST","\nкаже те саме про запит, який перелічує свої стовпці, а саме такий supabase-js\nнадсилає для масиву: \"any missing columns in the payload will be inserted as\n",[21,348,127],{}," value by default.\"",[10,351,352,353,356,357,359,360,363,364,366],{},"Тож тут друге завдання потрапляє з порожнім ",[21,354,355],{},"status",", навіть якщо таблиця дає\n",[21,358,355],{}," значення за замовчуванням ",[21,361,362],{},"'todo'",", і обовʼязковий ",[21,365,355],{}," відхиляє всю\nвставку:",[14,368,373],{"className":369,"code":371,"language":372,"meta":23},[370],"language-js","await supabase.from('tasks').insert([\n  { title: 'Написати бриф', status: 'doing' },\n  { title: 'Надіслати рахунок' },\n])\n","js",[21,374,371],{"__ignoreMap":23},[10,376,377,378,380],{},"Годиться будь-яке з двох виправлень. Надсилайте ",[21,379,355],{}," у кожному рядку або\nскажіть supabase-js лишити в силі значення таблиці за замовчуванням:",[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],{},"Цей параметр додає заголовок ",[21,391,392],{},"Prefer: missing=default",", який читає PostgREST.\nОдин рядок, переданий як обʼєкт без масиву довкола, так не робить: ключ, якого в\nньому немає, відсутній і у вставці, і спрацьовує значення за замовчуванням.",[395,396],"diagram",{"alt":397,"caption":398,"src":399},"Та сама вставка двох рядків, намальована двічі. Другий бланк пропускає своє поле status із зірочкою. Ліворуч, за defaultToNull: true, у цьому полі стоїть null бурштиновим поверх надрукованого значення за замовчуванням, і вставка закінчується на 23502. Праворуч, за defaultToNull: false, надруковане значення видно, і вставка має позначку.","У пакетній вставці стовпець, який є в одному рядку й відсутній в іншому, потрапляє в другий як null. defaultToNull: false дає заповнити його значенню таблиці за замовчуванням.","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fnull-over-the-default-1600x720.png",[39,401,403],{"id":402},"чому-upsert-падає-з-цим-хоча-рядок-уже-існує","Чому upsert падає з цим, хоча рядок уже існує?",[10,405,406],{},"Бо Postgres перевіряє новий рядок на порожні обовʼязкові стовпці раніше, ніж\nшукає наявний рядок, який мав би оновити. Upsert схожий на бланк із позначкою\n\"підшити, а якщо на цю людину вже є справа, змінити\". Postgres спершу дивиться на\nзірочки. Бланк, на якому лише нове фото профілю, повертається, хоча справа, яку\nвін мав змінити, лежить у шухляді.",[10,408,409,410,415,416,419],{},"Порядок записано в\n",[87,411,414],{"href":412,"rel":413},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Fexecutor\u002FnodeModifyTable.c",[91],"коді, що виконує вставку",":\n\"Check the constraints of the tuple.\" стоїть перед \"Perform a speculative\ninsertion.\", а це той крок, що знаходить конфлікт, на який реагує вираз\n",[21,417,418],{},"on conflict",". Tuple у Postgres означає рядок. Тож цей upsert, який мав змінити\nодин стовпець наявного профілю, падає на будь-якому іншому стовпці, що його\nвимагає таблиця:",[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],{},"Щоб змінити кілька стовпців рядка, який уже є, використовуйте update. Він\nпотрапляє на збережений рядок, у якому заповнено всі обовʼязкові стовпці, і змінює\nлише те, що ви надсилаєте:",[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],{},"Лишіть upsert для рядків, яких іще може не бути, і надсилайте з ним кожен\nобовʼязковий стовпець.\n",[87,439,441],{"href":440},"\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint","Допис про дублікати ключів","\nпояснює решту того, що потрібно upsert: за яким стовпцем він звіряє і які правила\nчитання зачіпає з браузера.",[395,444],{"alt":445,"caption":446,"src":447},"Дві смуги, що починаються з тієї самої зміни одного поля. У смузі upsert вона стає новим рядком, друге поле якого із зірочкою порожнє, шлагбаум із зірочкою закривається бурштиновим із 23502 під ним, а шлагбаум on conflict і шухляда за ним лишаються недосяжними. У смузі update вона потрапляє на збережений рядок з усіма заповненими полями із зірочкою, шлагбаум відкритий, і шухляда його приймає.","Рядок upsert перевіряють на порожні обовʼязкові стовпці раніше, ніж Postgres шукає рядок, який мав би оновити. Update починає зі збереженого рядка, де вони вже є.","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fupsert-is-checked-first-1600x780.png",[39,449,451],{"id":450},"чи-варто-прибрати-правило-not-null","Чи варто прибрати правило NOT NULL?",[10,453,454],{},"Лише якщо стовпець ніколи не мав бути обовʼязковим. Номер телефону, який ваша\nформа вважає необовʼязковим, саме такий випадок, і там прибрати правило означає\nвиправити правильно:",[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],{},"Для будь-якого іншого стовпця залиште правило й заповніть поле. Без нього кожен\nрядок, збережений потім, може лишитися без значення, яке правило гарантувало, і\nваш застосунок натрапить на пропуск деінде: сторінка, що читає\n",[21,467,468],{},"task.category.name",", зупиниться на ",[21,471,472],{},"Cannot read properties of null",", або підрахунок\nзавдань за категоріями мовчки загубить ті, що без категорії.",[10,475,476,477,479,480,483],{},"Найдорожче прибирати правило зі стовпця ",[21,478,256],{},". Рядок, збережений із\nпорожнім власником, ніхто не бачить. Правило читання, записане як\n",[21,481,482],{},"auth.uid() = user_id",", ніколи його не охоплює, бо в SQL порожнє значення не\nдорівнює нічому, навіть іншому порожньому значенню. Завдання збережене, і відтоді\nйого не бачить ніхто, навіть людина, яка його створила.",[10,485,486,487,489],{},"Виправлення для ",[21,488,256],{}," полягає в значенні за замовчуванням, яке підставляє\nтого, хто увійшов:",[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 обчислює значення за замовчуванням \"whenever the default value is\ninserted (not when the table is created)\",\n",[87,500,502],{"href":317,"rel":501},[91],"його власними словами",",\nтож кожен новий рядок отримує id людини, яка зберігає. Коли ніхто не увійшов,\n",[21,505,506],{},"auth.uid()"," повертає null, як попереджає\n",[87,509,512],{"href":510,"rel":511},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Fdatabase\u002Fpostgres\u002Frow-level-security",[91],"посібник Supabase з row level security",",\nі збереження все одно падає. Так правило робить свою роботу. Значення за\nзамовчуванням заповнює поле лише тоді, коли застосунок його пропускає, тож від\nтого, щоб хтось надіслав id іншої людини, і далі захищає ваше правило вставки.",[10,515,516,517,521,522,524,525,527,528,530,531,535,536,538,539,541,542,544],{},"Це правило вставки також змінює, яке повідомлення ви бачите. Postgres виконує\nперевірки Row Level Security таблиці на новому рядку раніше за перевірки\nnot-null:\n",[87,518,520],{"href":412,"rel":519},[91],"той самий код вставки","\nставить \"Check any RLS WITH CHECK policies.\" перед \"Check the constraints of the\ntuple.\". Тож коли ваше правило вставки порівнює ",[21,523,256],{}," з ",[21,526,506],{},", порожній\n",[21,529,256],{}," спершу не проходить правило й повертається як\n",[87,532,534],{"href":533},"\u002Fblog\u002Fnew-row-violates-row-level-security-policy","new row violates row-level security policy",".\n",[21,537,82],{}," на ",[21,540,256],{}," із вашого застосунку означає одне з трьох: правило вставки\nніколи не дивиться на ",[21,543,256],{},", правила на цій таблиці вимкнені, або запит\nприйшов із серверного коду, що їх оминає.",[39,546,548],{"id":547},"чи-безпечно-показувати-цю-помилку-користувачам","Чи безпечно показувати цю помилку користувачам?",[10,550,551],{},"Ні. Показуйте власне речення, а сиру помилку тримайте в логах, бо рядок деталей,\nколи Postgres його додає, є копією всього рядка.",[10,553,554,555,559,560,562],{},"Рядок деталей дубліката ключа повторює одне значення. Цей повторює весь бланк.\n",[87,556,558],{"href":176,"rel":557},[91],"Функція, яка його пише",",\nвиводить рядок як ",[21,561,123],{}," з кожним стовпцем, який той, хто\nвикликає, має право читати. Для update це рядок таким, яким його було б\nзбережено, разом зі стовпцями, яких ніхто не торкався. Бланк, який Postgres\nповертає, може мати пришпилену до відмови ксерокопію самого себе, і на копії видно\nкожне поле.",[10,564,565,566,569],{},"Postgres вирішує, хто отримає ксерокопію, і його код спершу перевіряє: \"Check if\nRLS is enabled and should be active for the relation; if so, then don't return\nanything.\" Запит із вашим публічним ключем до таблиці з увімкненою Row Level\nSecurity отримує ",[21,567,568],{},"\"details\": null",", і саме це показувала консоль на початку цієї\nсторінки.",[10,571,572],{},"Повну копію отримують двоє.",[10,574,575,578,579,582,583,586,587,591,592,596],{},[190,576,577],{},"Браузер, коли правила таблиці вимкнені."," Якщо консоль показує\n",[21,580,581],{},"Failing row contains"," для запиту, який ваш застосунок зробив із публічним\nключем, Row Level Security на цій таблиці вимкнена. Так само\n",[87,584,585],{"href":440},"допис про дублікати ключів","\nчитає свій рядок деталей, а\n",[87,588,590],{"href":589},"\u002Fblog\u002Fenable-rls-on-every-supabase-table","увімкнення правил на кожній таблиці","\nпоказує перемикач і перше правило. Таблиця з вимкненими правилами віддає свої\nрядки незнайомцю так само охоче, як вам. Наше\n",[87,593,595],{"href":594},"\u002Fsecurity-scanner","безкоштовне сканування"," читає ваш робочий застосунок із\nпублічним ключем, який отримує кожен відвідувач, і перелічує таблиці, що віддають\nрядки. Обліковий запис не потрібен.",[10,598,599,602,603,606,607,610,611,614],{},[190,600,601],{},"Серверний код із секретним ключем."," Посібник Supabase каже, що секретний ключ\n\"authorizes access through the ",[21,604,605],{},"service_role"," Postgres role, which has the\n",[21,608,609],{},"bypassrls"," attribute\", тож правила ніколи не діють, і рядок деталей завжди\nзаповнений. Edge Function, яка оновлює рядок і відповідає отриманою помилкою,\n",[21,612,613],{},"return new Response(JSON.stringify(error))",", надсилає в браузер кожен стовпець\nрядка, який вона змінювала, хоч би кому той рядок належав.",[10,616,617],{},"У серверному коді збережіть помилку й відповідайте власними словами:",[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: 'Не вдалося зберегти.' }), {\n    status: 400,\n  })\n}\n",[21,623,621],{"__ignoreMap":23},[10,625,626],{},"У застосунку перехопіть код і скажіть людині, що вона може зробити:",[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('Виберіть категорію, перш ніж зберегти.')\n} else if (error) {\n  setMessage('Щось пішло не так. Спробуйте ще раз.')\n}\n",[21,632,630],{"__ignoreMap":23},[10,634,635,639],{},[87,636,638],{"href":637},"\u002Fblog\u002Fmove-a-secret-to-an-edge-function","Перенесення секретного ключа в Edge Function","\nпояснює частину з функцією, якщо секретний ключ досі у вашому застосунку.",[395,641],{"alt":642,"caption":643,"src":644},"Той самий відхилений рядок, надісланий двічі в ту саму шухляду. Ліворуч, із вікна браузера, на шухляді висить замок, а відповідь містить code 23502, 400 і details null. Праворуч, із функції, що тримає ключ, замка на шухляді немає, і рядок details у відповіді, Failing row contains, бурштиновий, а під ним ряд бурштинових смуг, по одній на стовпець.","З увімкненою Row Level Security браузер не отримує копії рядка. Серверний код із секретним ключем отримує її повністю й передає далі, якщо повертає помилку такою, як вона прийшла.","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fthe-whole-row-comes-back-1600x760.png",[39,646,648],{"id":647},"перш-ніж-змінювати-правила-стовпця","Перш ніж змінювати правила стовпця",[10,650,651],{},"Зберігайте копію бази даних із часу, коли ви ще не додали значення за\nзамовчуванням, не прибрали правило й не записали значення в усі рядки, і\nдізнавайтеся, коли таблиця починає відповідати незнайомцям.",[10,653,654],{},[190,655,656,660],{},[87,657,659],{"href":658},"\u002Fsupabase-backups","Reeve Care"," зберігає копію вашої бази даних Supabase:",[662,663,664,668,671,674,677,680],"ul",{},[665,666,667],"li",{},"зашифрована копія за розкладом, у власному сховищі Reeve в ЄС, поза вашим обліковим записом Supabase",[665,669,670],{},"кожен рядок таким, яким він був до того, як update на всю таблицю його переписав",[665,672,673],{},"кожна копія перевірена, перш ніж її зараховують, підрахунком рядків у кожній таблиці",[665,675,676],{},"ваші завантажені файли теж, щойно ви підключите облікові дані Storage",[665,678,679],{},"відновлення одним кліком, перед яким береться страхувальна копія поточного стану",[665,681,682],{},"усе, що робить Monitor",[10,684,685],{},[190,686,687,691],{},[87,688,690],{"href":689},"\u002Fwebsite-monitoring","Reeve Monitor"," і далі перевіряє ззовні:",[662,693,694,697,700,703],{},[665,695,696],{},"чи доступний ваш застосунок, з перевіркою щохвилини",[665,698,699],{},"повна перевірка безпеки щогодини, до трьох застосунків",[665,701,702],{},"лист того дня, коли нове сканування оцінить ваш застосунок гірше за попереднє, тож таблиця, на якій вимкнули правила, дійде до вас, і вам не доведеться йти дивитися",[665,704,705],{},"щомісячний звіт простою мовою про те, що він побачив",[39,707,709],{"id":708},"що-зробити-зараз","Що зробити зараз",[711,712,713],"key-takeaways",{},[662,714,715,718,724,731,738,746],{},[665,716,717],{},"Прочитайте стовпець і таблицю в повідомленні. Це те поле, що прийшло порожнім.",[665,719,720,721,723],{},"Перевірте, чи ваш застосунок пропускає це поле, надсилає його як ",[21,722,127],{}," або чекає значення за замовчуванням, якого в стовпця немає.",[665,725,726,727,730],{},"Щоб змінити кілька стовпців наявного рядка, використовуйте ",[21,728,729],{},".update()"," з фільтром. Upsert мусить нести кожен обовʼязковий стовпець.",[665,732,733,734,737],{},"У пакетній вставці надсилайте кожен стовпець у кожному рядку або передайте ",[21,735,736],{},"{ defaultToNull: false }",", щоб діяли значення таблиці за замовчуванням.",[665,739,740,741,743,744,215],{},"Залиште правило на ",[21,742,256],{}," і дайте стовпцю значення за замовчуванням ",[21,745,506],{},[665,747,748],{},"У серверному коді записуйте сиру помилку в лог, а браузеру відповідайте власним реченням.",[10,750,751,752,755],{},"Почніть зі стовпця в повідомленні, потім перейдіть до рядка свого коду, який\nскладає рядок. Якщо виправлення змінює саму таблицю, нове значення за\nзамовчуванням чи update на всі рядки,\n",[87,753,754],{"href":658},"збережіть копію бази даних"," до того, як його запустите.",{"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},"Основи безпеки","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fcover-1200x630.png","Паперовий бланк перед відчиненою шухлядою картотеки, його друге поле із зірочкою порожнє й обведене бурштиновим.","Supabase \"null value in column violates not-null constraint\": обовʼязковий стовпець прийшов порожнім. Звідки взявся null і як це виправити.",false,"md",[775,778,781,784,787],{"q":776,"a":777},"Що означає null value in column violates not-null constraint?","Ваш застосунок спробував зберегти рядок без значення в стовпці, який завжди мусить його мати, і Postgres відхилив увесь рядок. Нічого не записано. Повідомлення називає стовпець і таблицю. Код 23502, а із застосунку на Supabase він приходить як HTTP 400.",{"q":779,"a":780},"Чому стовпець null, якщо в нього є значення за замовчуванням?","Бо запит назвав стовпець і надіслав його порожнім. Postgres підставляє значення за замовчуванням лише тоді, коли стовпець пропущено. supabase-js робить саме це в пакетній вставці: стовпець, який є в одному рядку й відсутній в іншому, потрапляє в другий як null, якщо ви не передасте defaultToNull: false.",{"q":782,"a":783},"Чому мій upsert падає з помилкою not-null, хоча рядок існує?","Postgres перевіряє новий рядок на порожні обовʼязкові стовпці раніше, ніж шукає наявний рядок, який мав би оновити. Upsert, що несе лише стовпці для зміни, падає на будь-якому іншому обовʼязковому стовпці. Для рядка, який уже існує, використовуйте update з фільтром.",{"q":785,"a":786},"Чи варто прибрати обмеження NOT NULL?","Лише якщо стовпець ніколи не мав бути обовʼязковим, як-от номер телефону, який ваша форма вважає необовʼязковим. Інакше помилка зникне, а рядки почнуть приходити без значення, на яке ваш застосунок натрапить деінде. Рядок із порожнім user_id не бачить ніхто. Зʼясуйте, звідки береться null, або дайте стовпцю значення за замовчуванням, наприклад auth.uid().",{"q":548,"a":788},"Ні. Коли Postgres додає рядок деталей, це копія всього рядка. Запит із браузера до таблиці з увімкненою Row Level Security його не отримує, а серверний код із секретним ключем отримує завжди. Перехоплюйте код 23502, показуйте речення, яке написали ви, а сиру помилку тримайте у своїх логах.","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fcard-800x500.png",[791,792,793,794,795,796,797],"null value in column violates not-null constraint","помилка not null supabase","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",[805,806,807],"Supabase \"null value in column violates not-null constraint\" (код Postgres 23502, HTTP 400 у вашому застосунку) означає, що рядок прийшов без нічого в стовпці, який завжди мусить мати значення. Повідомлення називає стовпець і таблицю. Нічого не збереглося.","Порожнє значення береться з поля, яке ваш застосунок ніколи не надсилає, з null, який ваш код надсилає навмисно, або зі стовпця без значення за замовчуванням, на яке ви розраховували. Upsert і пакетна вставка можуть спричинити його на рядку, що виглядає повним.","Залиште правило й виправте рядок. Із серверного коду ніколи не повертайте сиру помилку в браузер: її рядок \"Failing row contains\" може показати весь рядок.","aJ1mC1jatVTVqrFKAdQV7MtHo1haCowYAdeUX6QkviI",[810,816,822,828,829,835,841,847,853,859,865,871,877,883,889,895,901,906,912,918,923,929,935,941,947,953,959,964,970,976,982,988,994,1000,1006,1012,1017,1023,1029,1035,1041,1047,1053,1059,1065,1071,1076,1082,1088,1094,1100,1106,1112,1118,1124,1130,1136,1142,1148,1154,1160,1166,1171,1177,1183,1189,1195,1201,1207,1213,1219,1225,1231,1237,1243,1249,1254,1260,1266,1272,1278,1284,1290,1296,1301,1307,1313,1319,1324,1330],{"path":811,"title":812,"description":813,"published":814,"category":768,"image":815,"draft":772},"\u002Fblog\u002Fmax-client-connections-reached","Supabase \"max client connections reached\": пояснення","Supabase \"max client connections reached\": у пулера немає місця ще для одного клієнта. Ліміт визначає порт у вашому рядку підключення.","2026-11-09","\u002Fblog\u002Fmax-client-connections-reached\u002Fcard-800x500.png",{"path":817,"title":818,"description":819,"published":820,"category":768,"image":821,"draft":772},"\u002Fblog\u002Funsupported-provider-provider-is-not-enabled","Помилка Supabase: \"Unsupported provider: provider is not enabled\"","\"Unsupported provider: provider is not enabled\": вхід через Google вимкнено у вашому проєкті Supabase. Як увімкнути та виправити redirect_uri_mismatch.","2026-11-08","\u002Fblog\u002Funsupported-provider-provider-is-not-enabled\u002Fcard-800x500.png",{"path":823,"title":824,"description":825,"published":826,"category":768,"image":827,"draft":772},"\u002Fblog\u002Fviolates-foreign-key-constraint","Помилка Supabase \"violates foreign key constraint\" під час вставки","Supabase \"insert or update on table violates foreign key constraint\": ваш рядок посилається на рядок, якого немає. Який, чому і як це виправити.","2026-11-07","\u002Fblog\u002Fviolates-foreign-key-constraint\u002Fcard-800x500.png",{"path":800,"title":5,"description":771,"published":801,"category":768,"image":789,"draft":772},{"path":830,"title":831,"description":832,"published":833,"category":768,"image":834,"draft":772},"\u002Fblog\u002Fauth-session-missing","Supabase \"Auth session missing!\": куди поділася сесія","\"Auth session missing!\" означає, що supabase-js не знайшла жодної сесії й навіть не спитала Supabase. Куди поділася сесія і як це виправити.","2026-11-05","\u002Fblog\u002Fauth-session-missing\u002Fcard-800x500.png",{"path":836,"title":837,"description":838,"published":839,"category":768,"image":840,"draft":772},"\u002Fblog\u002Finvalid-claim-missing-sub-claim","Supabase \"invalid claim: missing sub claim\": ніхто не ввійшов","\"invalid claim: missing sub claim\": Supabase Auth отримав справжній токен без жодного користувача, ключ проєкту замість сесії.","2026-11-04","\u002Fblog\u002Finvalid-claim-missing-sub-claim\u002Fcard-800x500.png",{"path":842,"title":843,"description":844,"published":845,"category":768,"image":846,"draft":772},"\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function","\"Failed to send a request to the Edge Function\": справжня причина","\"Failed to send a request to the Edge Function\": браузер не отримав відповіді, яку можна прочитати. Його консоль каже, чому, і чи ваша функція запускалася.","2026-11-03","\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function\u002Fcard-800x500.png",{"path":848,"title":849,"description":850,"published":851,"category":768,"image":852,"draft":772},"\u002Fblog\u002Fsupabase-auth-redirects-to-localhost","Чому Supabase перенаправляє посилання з листів на localhost:3000","Якщо Supabase перенаправляє на localhost:3000, Site URL так і не змінили або вашої адреси немає в Redirect URLs. Як виправити і якої маски уникати.","2026-11-02","\u002Fblog\u002Fsupabase-auth-redirects-to-localhost\u002Fcard-800x500.png",{"path":854,"title":855,"description":856,"published":857,"category":768,"image":858,"draft":772},"\u002Fblog\u002Femail-link-is-invalid-or-has-expired","Supabase \"Email link is invalid or has expired\" на новому посиланні","На посиланні, надісланому хвилину тому, \"Email link is invalid or has expired\" у Supabase зазвичай означає, що поштовий фільтр використав його першим.","2026-11-01","\u002Fblog\u002Femail-link-is-invalid-or-has-expired\u002Fcard-800x500.png",{"path":860,"title":861,"description":862,"published":863,"category":768,"image":864,"draft":772},"\u002Fblog\u002Fcanceling-statement-due-to-statement-timeout","Supabase \"canceling statement due to statement timeout\": пояснення","Supabase \"canceling statement due to statement timeout\": запит перевищив ліміт своєї ролі, 3 секунди для відвідувача. Спершу перевірте політику RLS.","2026-10-31","\u002Fblog\u002Fcanceling-statement-due-to-statement-timeout\u002Fcard-800x500.png",{"path":305,"title":866,"description":867,"published":868,"category":869,"image":870,"draft":772},"\"Database error deleting user\" у Supabase і що зникає разом","\"Database error deleting user\" у Supabase: ваша база відмовилася видаляти. Причина в логах, а виправлення вирішує, що ще буде видалено.","2026-10-30","Backups","\u002Fblog\u002Fdatabase-error-deleting-user\u002Fcard-800x500.png",{"path":872,"title":873,"description":874,"published":875,"category":768,"image":876,"draft":772},"\u002Fblog\u002Fstripe-webhook-no-signatures-found","\"No signatures found matching the expected signature\" у Supabase","Вебхук Stripe в Edge Function Supabase пише \"No signatures found matching the expected signature\"? Причин дві. Не вимикайте перевірку.","2026-10-29","\u002Fblog\u002Fstripe-webhook-no-signatures-found\u002Fcard-800x500.png",{"path":878,"title":879,"description":880,"published":881,"category":768,"image":882,"draft":772},"\u002Fblog\u002Fsupabase-invalid-login","\"Invalid login credentials\" у Supabase: три причини","Supabase відповідає \"Invalid login credentials\" на неправильний пароль, невідому пошту й акаунт Google. Як зрозуміти, що саме.","2026-10-28","\u002Fblog\u002Fsupabase-invalid-login\u002Fcard-800x500.png",{"path":884,"title":885,"description":886,"published":887,"category":768,"image":888,"draft":772},"\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code","\"Edge Function returned a non-2xx status code\": справжня помилка","\"Edge Function returned a non-2xx status code\" означає, що ваша функція Supabase відповіла помилкою. Ось де прочитати справжню.","2026-10-27","\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code\u002Fcard-800x500.png",{"path":890,"title":891,"description":892,"published":893,"category":768,"image":894,"draft":772},"\u002Fblog\u002Fcertificate-not-trusted","Сертифікат не довірений: три причини, крім закінчення строку","Сертифікат не довірений, хоча дата в порядку? Він називає іншу адресу, підписав сам себе або в його ланцюжку бракує ланки. Як зрозуміти, що саме.","2026-10-26","\u002Fblog\u002Fcertificate-not-trusted\u002Fcard-800x500.png",{"path":896,"title":897,"description":898,"published":899,"category":768,"image":900,"draft":772},"\u002Fblog\u002Finvalid-input-syntax-for-type-uuid","Supabase \"invalid input syntax for type uuid\": що в лапках","Supabase \"invalid input syntax for type uuid\" означає, що застосунок надіслав щось, що не є id. Прочитайте лапки, притримайте запит, залиште колонку.","2026-10-25","\u002Fblog\u002Finvalid-input-syntax-for-type-uuid\u002Fcard-800x500.png",{"path":440,"title":902,"description":903,"published":904,"category":768,"image":905,"draft":772},"Supabase \"duplicate key value violates unique constraint\": пояснення","Supabase \"duplicate key value violates unique constraint\": значення вже зайняте. Знайдіть стовпець, оберіть виправлення й сховайте значення від очей.","2026-10-24","\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint\u002Fcard-800x500.png",{"path":907,"title":908,"description":909,"published":910,"category":768,"image":911,"draft":772},"\u002Fblog\u002Fsupabase-relation-does-not-exist","Supabase \"relation does not exist\", хоча таблиця на місці","Supabase \"relation does not exist\" або PGRST205: жодна таблиця не має такої назви в цій схемі. Чотири перевірки, і на що зважити, відкриваючи схему.","2026-10-23","\u002Fblog\u002Fsupabase-relation-does-not-exist\u002Fcard-800x500.png",{"path":913,"title":914,"description":915,"published":916,"category":768,"image":917,"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\" показує назви аргументів, які надіслав ваш застосунок. Порівняйте їх із функцією.","2026-10-22","\u002Fblog\u002Fsupabase-function-not-found\u002Fcard-800x500.png",{"path":213,"title":919,"description":920,"published":921,"category":768,"image":922,"draft":772},"Supabase PGRST204: \"Could not find the column in the schema cache\"","Supabase \"Could not find the column in the schema cache\" зазвичай означає, що назва не збігається. Чотири перевірки і рядок для перезавантаження.","2026-10-21","\u002Fblog\u002Fsupabase-schema-cache-column-error\u002Fcard-800x500.png",{"path":924,"title":925,"description":926,"published":927,"category":768,"image":928,"draft":772},"\u002Fblog\u002Fsupabase-email-rate-limit-exceeded","Supabase \"Email Rate Limit Exceeded\": два ліміти, один код помилки","Supabase \"Email Rate Limit Exceeded\" обмежує кожен лист вашого проєкту: 2 на годину з вбудованим відправником. Що враховується і як підняти ліміт.","2026-10-20","\u002Fblog\u002Fsupabase-email-rate-limit-exceeded\u002Fcard-800x500.png",{"path":930,"title":931,"description":932,"published":933,"category":768,"image":934,"draft":772},"\u002Fblog\u002Fsupabase-users-logged-out","Supabase \"Invalid Refresh Token: Refresh Token Not Found\": 3 причини","Supabase \"Invalid Refresh Token: Refresh Token Not Found\" виводить ваших користувачів із системи. Три причини і як лог Auth їх розрізняє.","2026-10-19","\u002Fblog\u002Fsupabase-users-logged-out\u002Fcard-800x500.png",{"path":936,"title":937,"description":938,"published":939,"category":768,"image":940,"draft":772},"\u002Fblog\u002Fpermission-denied-for-schema-public","Supabase \"permission denied for schema public\": три причини","Supabase \"permission denied for schema public\" означає, що бракує GRANT на схему. Що його прибрало і який рядок його повертає.","2026-10-18","\u002Fblog\u002Fpermission-denied-for-schema-public\u002Fcard-800x500.png",{"path":942,"title":943,"description":944,"published":945,"category":768,"image":946,"draft":772},"\u002Fblog\u002Fsupabase-bucket-not-found","Supabase \"Bucket not found\": три речі, які це може означати","Supabase \"Bucket not found\" означає одне з трьох: публічне посилання на приватний бакет, неточну назву або не той проєкт.","2026-10-17","\u002Fblog\u002Fsupabase-bucket-not-found\u002Fcard-800x500.png",{"path":948,"title":949,"description":950,"published":951,"category":768,"image":952,"draft":772},"\u002Fblog\u002Fsupabase-invalid-jwt","Supabase invalid JWT: який ключ перестав працювати?","Помилка Supabase invalid JWT — це PGRST301: підписаний токен не пройшов перевірку. У новому проєкті йдеться про сесію, а не про ключ API.","2026-10-16","\u002Fblog\u002Fsupabase-invalid-jwt\u002Fcard-800x500.png",{"path":954,"title":955,"description":956,"published":957,"category":768,"image":958,"draft":772},"\u002Fblog\u002Fsupabase-single-row-error","Supabase \"multiple (or no) rows returned\" майже завжди означає нуль","Supabase повертає PGRST116, \"JSON object requested, multiple (or no) rows returned\", коли запит нічого не знайшов. Прихований рядок має вигляд відсутнього.","2026-10-15","\u002Fblog\u002Fsupabase-single-row-error\u002Fcard-800x500.png",{"path":287,"title":960,"description":961,"published":962,"category":768,"image":963,"draft":772},"\"Database error saving new user\" у Supabase: де шукати","\"Database error saving new user\" означає, що щось причеплене до вашої реєстрації в Supabase впало. Справжня помилка лежить у ваших логах.","2026-10-14","\u002Fblog\u002Fdatabase-error-saving-new-user\u002Fcard-800x500.png",{"path":965,"title":966,"description":967,"published":968,"category":768,"image":969,"draft":772},"\u002Fblog\u002Fwhat-is-supabase","Що таке Supabase? База даних, яку обрав ваш білдер","Що таке Supabase: база даних, вхід і сховище файлів за вашим застосунком. Supabase тримає сервери, а правила про те, хто читає ваші рядки, ваші.","2026-10-13","\u002Fblog\u002Fwhat-is-supabase\u002Fcard-800x500.png",{"path":971,"title":972,"description":973,"published":974,"category":768,"image":975,"draft":772},"\u002Fblog\u002Faws-access-key-exposed-in-frontend","Відкритий у фронтенді ключ AWS це не те саме, що ключ OpenAI","Ключ доступу AWS, відкритий у вашому фронтенді, читає і пише ваше сховище. Рішення зазвичай presigned URL, а не новий ключ.","2026-10-12","\u002Fblog\u002Faws-access-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":977,"title":978,"description":979,"published":980,"category":768,"image":981,"draft":772},"\u002Fblog\u002Fstorage-upload-violates-row-level-security","\"Row-level security policy for table objects\" при вивантаженні","\"New row violates row-level security policy for table objects\" означає, що вашому вивантаженню бракує правила insert. Публічний бакет його не додає.","2026-10-11","\u002Fblog\u002Fstorage-upload-violates-row-level-security\u002Fcard-800x500.png",{"path":983,"title":984,"description":985,"published":986,"category":768,"image":987,"draft":772},"\u002Fblog\u002Fbase44-security-scan","Сканування безпеки Base44: те, що бачить лише воно","Сканування безпеки Base44 перевіряє сім видів проблем зсередини. Ось половина, яку читає тільки воно, і половина, на яку воно ніколи не дивиться.","2026-10-10","\u002Fblog\u002Fbase44-security-scan\u002Fcard-800x500.png",{"path":989,"title":990,"description":991,"published":992,"category":768,"image":993,"draft":772},"\u002Fblog\u002Fapi-key-leaked-what-to-do","Ваш API-ключ витік. Порядок дій","API-ключ витік, і ви не знаєте, що робити першим. Не кожен ключ у вашому фронтенді є витоком, а порядок важить більше за швидкість.","2026-10-09","\u002Fblog\u002Fapi-key-leaked-what-to-do\u002Fcard-800x500.png",{"path":995,"title":996,"description":997,"published":998,"category":768,"image":999,"draft":772},"\u002Fblog\u002Finfinite-recursion-in-policy-for-relation","\"Infinite recursion detected in policy\" без вимкнення RLS","\"Infinite recursion detected in policy for relation\" означає, що ваша policy запитала таблицю, яку вона захищає. Ось як розірвати коло.","2026-10-08","\u002Fblog\u002Finfinite-recursion-in-policy-for-relation\u002Fcard-800x500.png",{"path":1001,"title":1002,"description":1003,"published":1004,"category":768,"image":1005,"draft":772},"\u002Fblog\u002Fno-api-key-found-in-request","\"No API key found in request\" у Supabase, і хибне виправлення","\"No API key found in request\" означає, що запит дійшов до Supabase без ключа. Більшість відповідей натомість вказують на правила вашої бази.","2026-10-07","\u002Fblog\u002Fno-api-key-found-in-request\u002Fcard-800x500.png",{"path":1007,"title":1008,"description":1009,"published":1010,"category":768,"image":1011,"draft":772},"\u002Fblog\u002Fis-base44-safe","Чи безпечний Base44? Дані 5 442 застосунків","Чи безпечний Base44? Ми прогнали дев’ять перевірок на 5 442 робочих застосунках Base44. Майже всі отримали B з причин, яких власник ніколи не обирав.","2026-10-06","\u002Fblog\u002Fis-base44-safe\u002Fcard-800x500.png",{"path":637,"title":1013,"description":1014,"published":1015,"category":768,"image":1016,"draft":772},"Сховати ключ API: перенесіть його в Supabase Edge Function","Сховати ключ API означає прибрати його з браузера, а Supabase Edge Function це найменше місце, куди його покласти. Два кроки поруч важать більше.","2026-10-05","\u002Fblog\u002Fmove-a-secret-to-an-edge-function\u002Fcard-800x500.png",{"path":1018,"title":1019,"description":1020,"published":1021,"category":768,"image":1022,"draft":772},"\u002Fblog\u002Fenv-file-exposed-on-your-server","Чи відкритий ваш файл .env? Дванадцять шляхів для перевірки","Чи відкритий ваш файл .env на вашому власному вебсервері? Дванадцять адрес скажуть це за хвилину, а влучання означає, що все в ньому вже публічне.","2026-10-04","\u002Fblog\u002Fenv-file-exposed-on-your-server\u002Fcard-800x500.png",{"path":1024,"title":1025,"description":1026,"published":1027,"category":768,"image":1028,"draft":772},"\u002Fblog\u002Fis-v0-safe","Безпека v0: усі 1 790 просканованих застосунків v0 отримали A","Безпека v0 на 1 790 живих застосунках v0: усі отримали A. Лише 17 згадували базу даних, і саме це здебільшого вимірює оцінка A.","2026-10-03","\u002Fblog\u002Fis-v0-safe\u002Fcard-800x500.png",{"path":1030,"title":1031,"description":1032,"published":1033,"category":768,"image":1034,"draft":772},"\u002Fblog\u002Fis-bolt-safe","Чи безпечний Bolt? Що показали 1 123 застосунки Bolt","Чи безпечний Bolt? Ми прогнали 1 123 живі застосунки Bolt через дев’ять перевірок. Хостинг був чистим. Знахідки: ключі й відкриті таблиці в самих застосунках.","2026-10-02","\u002Fblog\u002Fis-bolt-safe\u002Fcard-800x500.png",{"path":1036,"title":1037,"description":1038,"published":1039,"category":768,"image":1040,"draft":772},"\u002Fblog\u002Fis-supabase-down-or-your-app","Supabase не працює чи проблема у вашому застосунку? Як розрізнити","Supabase не працює чи зламався ваш застосунок? Пауза, повна база й ваша власна помилка виглядають однаково. Чотири перевірки їх розрізняють.","2026-10-01","\u002Fblog\u002Fis-supabase-down-or-your-app\u002Fcard-800x500.png",{"path":1042,"title":1043,"description":1044,"published":1045,"category":768,"image":1046,"draft":772},"\u002Fblog\u002Fdoes-supabase-encrypt-my-data","Чи шифрує Supabase мої дані? Так. Ось від чого це захищає","Чи шифрує Supabase дані? Так: AES-256 у стані спокою, TLS під час передавання, SOC 2 та ISO 27001. Що покриває кожне і який витік не зупиняє жодне.","2026-09-30","\u002Fblog\u002Fdoes-supabase-encrypt-my-data\u002Fcard-800x500.png",{"path":1048,"title":1049,"description":1050,"published":1051,"category":768,"image":1052,"draft":772},"\u002Fblog\u002Fsupabase-free-plan-limits","Ліміти безкоштовного тарифу Supabase і що стається на кожному","Ліміти безкоштовного тарифу Supabase: 500 МБ бази, 5 ГБ egress і жодних резервних копій. Що стається на кожній межі і що змінює Pro за $25.","2026-09-29","\u002Fblog\u002Fsupabase-free-plan-limits\u002Fcard-800x500.png",{"path":1054,"title":1055,"description":1056,"published":1057,"category":869,"image":1058,"draft":772},"\u002Fblog\u002Ftest-your-supabase-backup","Перевірте резервну копію Supabase до того, як вона знадобиться","Як перевірити резервну копію Supabase: відновити в тестовий проєкт, порахувати рядки, увійти й знайти рядок, якого бракує обрізаному файлу.","2026-09-28","\u002Fblog\u002Ftest-your-supabase-backup\u002Fcard-800x500.png",{"path":1060,"title":1061,"description":1062,"published":1063,"category":869,"image":1064,"draft":772},"\u002Fblog\u002Fsupabase-backup-github-action","Безкоштовна резервна копія Supabase через GitHub Action і підступ","Резервна копія Supabase через GitHub Action безкоштовна й годиться багатьом застосункам. Робочий процес, потрібний рядок підключення та egress.","2026-09-27","\u002Fblog\u002Fsupabase-backup-github-action\u002Fcard-800x500.png",{"path":1066,"title":1067,"description":1068,"published":1069,"category":869,"image":1070,"draft":772},"\u002Fblog\u002Fdownload-your-supabase-backup","Чому ви не можете завантажити резервну копію Supabase","Завантажити резервну копію Supabase на сучасному проєкті не вийде: щоденна копія там фізична. Як це перевірити і як тримати власну копію.","2026-09-26","\u002Fblog\u002Fdownload-your-supabase-backup\u002Fcard-800x500.png",{"path":1072,"title":1073,"description":1074,"published":1069,"category":768,"image":1075,"draft":772},"\u002Fblog\u002Fsupabase-permission-denied-for-table","Supabase \"permission denied for table\": бракує GRANT","З 30 жовтня нова таблиця в Supabase відповідає \"permission denied for table\", доки ви не дасте доступ. Лист показує лише половину виправлення.","\u002Fblog\u002Fsupabase-permission-denied-for-table\u002Fcard-800x500.png",{"path":1077,"title":1078,"description":1079,"published":1080,"category":768,"image":1081,"draft":772},"\u002Fblog\u002Fopen-api-endpoint-exposed","Чи є відкритий API-ендпоінт проблемою безпеки? Читайте JSON","Ваше сканування позначило відкритий API-ендпоінт. Чи це важливо, залежить від того, що повернулося, і більшість знайдених належали платформі.","2026-09-25","\u002Fblog\u002Fopen-api-endpoint-exposed\u002Fcard-800x500.png",{"path":1083,"title":1084,"description":1085,"published":1086,"category":768,"image":1087,"draft":772},"\u002Fblog\u002Flovable-security-scan","Сканування безпеки Lovable: чого воно не доведе","Сканування безпеки Lovable: що перевіряють Quick scan і Deep scan, коли запускається кожне, і єдине, чого не доведе жодне сканування зсередини.","2026-09-24","\u002Fblog\u002Flovable-security-scan\u002Fcard-800x500.png",{"path":1089,"title":1090,"description":1091,"published":1092,"category":869,"image":1093,"draft":772},"\u002Fblog\u002Fsupabase-backup-auth-users","Чому у вашому дампі Supabase немає користувачів","Запустіть supabase db dump окремо і отримаєте форму вашої бази та жодного її рядка, без схеми auth, у якій живуть ваші користувачі.","2026-09-23","\u002Fblog\u002Fsupabase-backup-auth-users\u002Fcard-800x500.png",{"path":1095,"title":1096,"description":1097,"published":1098,"category":768,"image":1099,"draft":772},"\u002Fblog\u002Fdomain-and-certificate-expiry","Домен закінчився, сайт лежить: що буде далі","Ваш домен закінчився і сайт лежить. Ось який годинник уже пішов, чому прострочений сертифікат це менша з двох бід, і як перевірити обидва.","2026-09-22","\u002Fblog\u002Fdomain-and-certificate-expiry\u002Fcard-800x500.png",{"path":1101,"title":1102,"description":1103,"published":1104,"category":768,"image":1105,"draft":772},"\u002Fblog\u002Fis-lovable-safe","Чи безпечний Lovable? Що показали 18 554 застосунки Lovable","Чи безпечний Lovable? Ми прогнали 18 554 живі застосунки Lovable через дев’ять перевірок. Платформа виявилася найчистішою з п’яти. Знахідки були в застосунках.","2026-09-21","\u002Fblog\u002Fis-lovable-safe\u002Fcard-800x500.png",{"path":1107,"title":1108,"description":1109,"published":1110,"category":768,"image":1111,"draft":772},"\u002Fblog\u002Fvibe-coded-app-security-checklist","Чек-лист безпеки для vibe coding, у девʼяти перевірках","Чек-лист безпеки для vibe coding із девʼяти пунктів, кожен з яких будь-хто може перевірити у вашому живому застосунку ззовні, і кожен має тест на один рядок.","2026-09-20","\u002Fblog\u002Fvibe-coded-app-security-checklist\u002Fcard-800x500.png",{"path":1113,"title":1114,"description":1115,"published":1116,"category":768,"image":1117,"draft":772},"\u002Fblog\u002Fstripe-secret-key-in-frontend","Секретний ключ Stripe у фронтенді може рухати гроші","Секретний ключ Stripe, відкритий у вашому фронтенді, може повертати кошти, списувати й читати кожну картку клієнта. А ключ pk_live_ там і має бути.","2026-09-19","\u002Fblog\u002Fstripe-secret-key-in-frontend\u002Fcard-800x500.png",{"path":1119,"title":1120,"description":1121,"published":1122,"category":768,"image":1123,"draft":772},"\u002Fblog\u002Fvite-and-next-public-env-vars","Змінні оточення Vite відкриті: префікс означає опублікувати це","Змінні оточення Vite відкриті у вашому застосунку, бо так просив префікс. VITE_ і NEXT_PUBLIC_ означають опублікувати це, а ШІ, який їх додав, не знав ціни.","2026-09-18","\u002Fblog\u002Fvite-and-next-public-env-vars\u002Fcard-800x500.png",{"path":1125,"title":1126,"description":1127,"published":1128,"category":768,"image":1129,"draft":772},"\u002Fblog\u002Fbase44-source-maps","Безпека Base44: що позначає скан і що з цього виправляти вам","Безпека Base44 на 5 438 просканованих застосунках: три знахідки майже на кожному належать платформі, source map є бейджем Base44, а ваша частина коротка.","2026-09-17","\u002Fblog\u002Fbase44-source-maps\u002Fcard-800x500.png",{"path":1131,"title":1132,"description":1133,"published":1134,"category":768,"image":1135,"draft":772},"\u002Fblog\u002Fis-cursor-ai-safe","Чи безпечний Cursor AI? Редактор, код і опублікований застосунок","Чи безпечний Cursor AI? Три запитання в одному пошуку: що Cursor зберігає, у чому помиляється його код і чи відкритий застосунок, який ви опублікували.","2026-09-16","\u002Fblog\u002Fis-cursor-ai-safe\u002Fcard-800x500.png",{"path":1137,"title":1138,"description":1139,"published":1140,"category":768,"image":1141,"draft":772},"\u002Fblog\u002Fis-replit-safe","Чи безпечний Replit? Що ми знайшли в 3 042 живих застосунках Replit","Чи безпечний Replit? Ми прогнали 3 042 живі застосунки Replit через дев’ять зовнішніх перевірок. Знахідки були в опублікованому застосунку, а не в хостингу.","2026-09-15","\u002Fblog\u002Fis-replit-safe\u002Fcard-800x500.png",{"path":1143,"title":1144,"description":1145,"published":1146,"category":869,"image":1147,"draft":772},"\u002Fblog\u002Fsupabase-storage-backup","Supabase storage backup: у копії бази даних немає жодного файлу","Supabase storage backup це окрема робота. Копії бази даних зберігають список ваших файлів і жодного з них, тож після відновлення кожне завантаження зламане.","2026-09-14","\u002Fblog\u002Fsupabase-storage-backup\u002Fcard-800x500.png",{"path":1149,"title":1150,"description":1151,"published":1152,"category":869,"image":1153,"draft":772},"\u002Fblog\u002Fsupabase-point-in-time-recovery","Point-in-time recovery у Supabase: ціна та межі","Point-in-time recovery у Supabase відмотує базу на будь-яку секунду за останній тиждень. Коштує $100 на місяць понад Pro і покриває лише базу даних.","2026-09-13","\u002Fblog\u002Fsupabase-point-in-time-recovery\u002Fcard-800x500.png",{"path":1155,"title":1156,"description":1157,"published":1158,"category":869,"image":1159,"draft":772},"\u002Fblog\u002Fsupabase-project-paused-recover","Проєкт Supabase на паузі? Ваші дані нікуди не зникли","Supabase поставив ваш проєкт на паузу після тижня без активності. Нічого не видалено, кнопка Restore стоїть поруч із назвою, і у вас є рік, щоб натиснути.","2026-09-12","\u002Fblog\u002Fsupabase-project-paused-recover\u002Fcard-800x500.png",{"path":1161,"title":1162,"description":1163,"published":1164,"category":768,"image":1165,"draft":772},"\u002Fblog\u002Fsafest-ai-app-builder","Який білдер застосунків з ШІ найбезпечніший? 30 998 тестів","Який білдер застосунків з ШІ найбезпечніший? Ми просканували 30 998 живих застосунків з Lovable, Base44, Replit, v0 і Bolt. Оцінку вирішує не білдер.","2026-09-11","\u002Fblog\u002Fsafest-ai-app-builder\u002Fcard-800x500.png",{"path":589,"title":1167,"description":1168,"published":1169,"category":768,"image":1170,"draft":772},"Увімкніть Row Level Security на кожній таблиці Supabase і перевірте","Row Level Security у Supabase без політики закриває таблицю повністю. Політика без увімкненого перемикача не робить нічого. Ось SQL, і ось перевірка.","2026-09-10","\u002Fblog\u002Fenable-rls-on-every-supabase-table\u002Fcard-800x500.png",{"path":1172,"title":1173,"description":1174,"published":1175,"category":768,"image":1176,"draft":772},"\u002Fblog\u002Fsupabase-rls-disabled-in-public","Supabase \"RLS disabled in public\": чого не бачить попередження","Supabase позначає \"RLS disabled in public\" як помилку. Про політику читання, яка лишає вашу таблицю так само відкритою, він не каже нічого.","2026-09-09","\u002Fblog\u002Fsupabase-rls-disabled-in-public\u002Fcard-800x500.png",{"path":1178,"title":1179,"description":1180,"published":1181,"category":768,"image":1182,"draft":772},"\u002Fblog\u002Frotate-supabase-service-role-key","Як замінити злитий ключ service_role у Supabase","Supabase каже спершу закрити витік. Інші посібники кажуть міняти негайно. Що правильно, залежить від того, куди саме потрапив ваш ключ service_role.","2026-09-08","\u002Fblog\u002Frotate-supabase-service-role-key\u002Fcard-800x500.png",{"path":1184,"title":1185,"description":1186,"published":1187,"category":768,"image":1188,"draft":772},"\u002Fblog\u002Fvibe-coding-security-scanners-compared","Сканери безпеки для vibe coding у порівнянні, наш теж","Найкращий сканер безпеки для vibe coding залежить від трьох питань, на які не відповідає жоден перелік функцій. Вісім інструментів і ціни.","2026-09-07","\u002Fblog\u002Fvibe-coding-security-scanners-compared\u002Fcard-800x500.png",{"path":1190,"title":1191,"description":1192,"published":1193,"category":768,"image":1194,"draft":772},"\u002Fblog\u002Fsupabase-security-checker","Supabase security checker: п’ять перевірок, які зробите самі","Supabase security checker читає ваш опублікований застосунок, а не налаштування проєкту. Ось п’ять перевірок і як виконати кожну самостійно.","2026-09-06","\u002Fblog\u002Fsupabase-security-checker\u002Fcard-800x500.png",{"path":1196,"title":1197,"description":1198,"published":1199,"category":768,"image":1200,"draft":772},"\u002Fblog\u002Fvibe-coding-security-scanner","Сканер безпеки для vibe coding: чого не бачить скан URL","Сканер безпеки для vibe coding читає ваш робочий застосунок ззовні. Що це охоплює, чотири речі, яких він не бачить, і як читати результат.","2026-09-05","\u002Fblog\u002Fvibe-coding-security-scanner\u002Fcard-800x500.png",{"path":1202,"title":1203,"description":1204,"published":1205,"category":768,"image":1206,"draft":772},"\u002Fblog\u002Fmissing-security-headers","Відсутні заголовки безпеки: коли це справді важливо","Відсутні заголовки безпеки трапляються в наших звітах найчастіше. Від чого вони захищають і коли це найменш терміновий рядок вашого звіту.","2026-09-04","\u002Fblog\u002Fmissing-security-headers\u002Fcard-800x500.png",{"path":1208,"title":1209,"description":1210,"published":1211,"category":768,"image":1212,"draft":772},"\u002Fblog\u002Fopenai-api-key-exposed-in-frontend","Ваш ключ API OpenAI відкритий у фронтенді. Замініть його.","Ключ API OpenAI, відкритий у фронтенді, не можна прив'язати до домену. Замініть його сьогодні, перенесіть виклик на свій сервер і обмежте витрати.","2026-09-03","\u002Fblog\u002Fopenai-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":1214,"title":1215,"description":1216,"published":1217,"category":768,"image":1218,"draft":772},"\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps","Ключ API у фронтенді: що насправді віддають 30 998 застосунків","Ключ API, відкритий у вашому фронтенді, зазвичай є ключем Google Maps. Ми просканували 30 998 vibe-coded застосунків і порахували, що витікає насправді.","2026-09-02","\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps\u002Fcard-800x500.png",{"path":1220,"title":1221,"description":1222,"published":1223,"category":869,"image":1224,"draft":772},"\u002Fblog\u002Fsupabase-backup-tools-compared","Інструменти резервного копіювання Supabase, і наш серед них","Чотири види інструментів резервного копіювання Supabase, що копіює кожен і коли безкоштовна GitHub Action виграє в будь-якої оплати, нашої теж.","2026-09-01","\u002Fblog\u002Fsupabase-backup-tools-compared\u002Fcard-800x500.png",{"path":1226,"title":1227,"description":1228,"published":1229,"category":768,"image":1230,"draft":772},"\u002Fblog\u002Freplit-secrets-explained","Як користуватися секретами в Replit і що все одно публікується","Як користуватися секретами в Replit: додати, прочитати і усунути дві причини undefined. А ще ключі, які цей інструмент не здатен зберегти в таємниці.","2026-08-31","\u002Fblog\u002Freplit-secrets-explained\u002Fcard-800x500.png",{"path":1232,"title":1233,"description":1234,"published":1235,"category":869,"image":1236,"draft":772},"\u002Fblog\u002Fback-up-supabase-free-tier","Резервні копії на безкоштовному тарифі Supabase без терміналу","На безкоштовному тарифі Supabase резервних копій немає: копію робите ви. Як зробити її з панелі та що втрачає шлях через CSV.","2026-08-30","\u002Fblog\u002Fback-up-supabase-free-tier\u002Fcard-800x500.png",{"path":1238,"title":1239,"description":1240,"published":1241,"category":768,"image":1242,"draft":772},"\u002Fblog\u002Fis-supabase-secure","Чи безпечний Supabase? Так. Ваш проєкт це інше питання","Чи безпечний Supabase? Платформа проходить аудити, шифрує дані й тримає пентести. Їхня документація каже, де це закінчується і починаються ваші налаштування.","2026-08-29","\u002Fblog\u002Fis-supabase-secure\u002Fcard-800x500.png",{"path":1244,"title":1245,"description":1246,"published":1247,"category":768,"image":1248,"draft":772},"\u002Fblog\u002Fwhere-to-find-supabase-api-keys","Де знайти API-ключі Supabase: anon, service_role і URL","URL проєкту, ключ anon і ключ service_role лежать на одній сторінці панелі. Ось де вона, і яке з чотирьох значень має бути у вашому застосунку.","2026-08-28","\u002Fblog\u002Fwhere-to-find-supabase-api-keys\u002Fcard-800x500.png",{"path":533,"title":1250,"description":1251,"published":1252,"category":768,"image":1253,"draft":772},"New row violates row-level security policy в Supabase. Що далі?","\"New row violates row-level security policy\" означає, що Supabase відмовив у записі. Швидке виправлення знову відкриває таблицю для всіх.","2026-08-27","\u002Fblog\u002Fnew-row-violates-row-level-security-policy\u002Fcard-800x500.png",{"path":1255,"title":1256,"description":1257,"published":1258,"category":768,"image":1259,"draft":772},"\u002Fblog\u002Fcors-wildcard-security-risk","Чи є вайлдкард CORS ризиком для безпеки? Майже ніколи.","Чи є вайлдкард CORS ризиком для безпеки? Зазвичай це типове налаштування вашого білдера, і воно не видає нічого, що ваш сервер і так віддавав кожному.","2026-08-26","\u002Fblog\u002Fcors-wildcard-security-risk\u002Fcard-800x500.png",{"path":1261,"title":1262,"description":1263,"published":1264,"category":869,"image":1265,"draft":772},"\u002Fblog\u002Fsupabase-branching-is-not-a-backup","Branching у Supabase це не бекап. Він іде тільки вперед.","Чому branching у Supabase це не бекап: гілка стартує без ваших даних, а мердж переносить саму лише схему. Для чого він потрібен і що брати замість нього.","2026-08-25","\u002Fblog\u002Fsupabase-branching-is-not-a-backup\u002Fcard-800x500.png",{"path":1267,"title":1268,"description":1269,"published":1270,"category":869,"image":1271,"draft":772},"\u002Fblog\u002Fhow-to-restore-a-supabase-backup","Як відновити резервну копію Supabase, і що ламається потім","Як відновити резервну копію Supabase з панелі або з файлу дампа, що саме замінює відновлення і чому ваш застосунок може лишитися зламаним після нього.","2026-08-24","\u002Fblog\u002Fhow-to-restore-a-supabase-backup\u002Fcard-800x500.png",{"path":1273,"title":1274,"description":1275,"published":1276,"category":768,"image":1277,"draft":772},"\u002Fblog\u002Fsupabase-storage-bucket-public","Ваш бакет Supabase Storage публічний. Чи це проблема?","Публічний бакет Supabase Storage означає, що той, хто має URL файлу, може його відкрити. Він не означає, що хтось може переглянути список вмісту.","2026-08-23","\u002Fblog\u002Fsupabase-storage-bucket-public\u002Fcard-800x500.png",{"path":1279,"title":1280,"description":1281,"published":1282,"category":768,"image":1283,"draft":772},"\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend","Чи є проблемою ключ API Google у вашому браузері?","Ключ API Google у відкритому коді сторінки наш сканер знаходить найчастіше, і зазвичай усе гаразд. Вирішує це одне безкоштовне налаштування.","2026-08-22","\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":1285,"title":1286,"description":1287,"published":1288,"category":869,"image":1289,"draft":772},"\u002Fblog\u002Fai-agent-deleted-my-database","ШІ-агент видалив мої дані в Supabase. Що можна відновити?","ШІ-агент видалив дані з вашої бази. Те, що ви зможете відновити, вирішилося раніше, а наступні хвилини вирішують, скільки з цього вціліє.","2026-08-21","\u002Fblog\u002Fai-agent-deleted-my-database\u002Fcard-800x500.png",{"path":1291,"title":1292,"description":1293,"published":1294,"category":768,"image":1295,"draft":772},"\u002Fblog\u002Fcan-anyone-read-your-supabase-database","Чи може будь-хто прочитати вашу базу Supabase? 3 680 застосунків","Чи може будь-хто прочитати вашу базу даних Supabase без входу? Ми просканували 30 998 живих застосунків, зроблених ШІ-білдерами, і виміряли, як часто так.","2026-08-18","\u002Fblog\u002Fcan-anyone-read-your-supabase-database\u002Fcard-800x500.png",{"path":1297,"title":1298,"description":1299,"published":1294,"category":768,"image":1300,"draft":772},"\u002Fblog\u002Fsource-maps-exposed-in-production","Відкриті source maps: ваш застосунок публікує свій вихідний код","Відкрита source map дає будь-кому прочитати оригінальний код вашого застосунку, разом із коментарями. Перевірка за 30 секунд, і що важить насправді.","\u002Fblog\u002Fsource-maps-exposed-in-production\u002Fcard-800x500.png",{"path":1302,"title":1303,"description":1304,"published":1305,"category":768,"image":1306,"draft":772},"\u002Fblog\u002Fsupabase-new-api-keys","Supabase publishable key: це той самий anon key?","Supabase замінив anon і service_role публічними та секретними ключами. Який із них має бути у вашому застосунку, а який не має бути ніколи?","2026-08-12","\u002Fblog\u002Fsupabase-new-api-keys\u002Fcard-800x500.png",{"path":1308,"title":1309,"description":1310,"published":1311,"category":869,"image":1312,"draft":772},"\u002Fblog\u002Fdoes-supabase-back-up-my-database","Чи робить Supabase резервні копії моєї бази? Залежить від тарифу.","Чи робить Supabase резервні копії вашої бази даних? На платних тарифах щодня, на безкоштовному жодної. Як дізнатися, що у вас насправді є.","2026-08-11","\u002Fblog\u002Fdoes-supabase-back-up-my-database\u002Fcard-800x500.png",{"path":1314,"title":1315,"description":1316,"published":1317,"category":768,"image":1318,"draft":772},"\u002Fblog\u002Fsupabase-rls-on-but-table-still-public","Supabase Row Level Security увімкнено. Таблиця досі публічна.","Увімкнути Supabase Row Level Security не означає захистити таблицю. Захищають політики, а та, що полагодила застосунок, часто впускає всіх.","2026-08-10","\u002Fblog\u002Fsupabase-rls-on-but-table-still-public\u002Fcard-800x500.png",{"path":1320,"title":1321,"description":1322,"published":1317,"category":869,"image":1323,"draft":772},"\u002Fblog\u002Fversion-history-is-not-a-backup","Історія версій не є резервною копією. Вона не поверне таблицю.","Lovable і Bolt ведуть історію версій вашого коду. Ваша база даних працює як окремий сервіс, тож відкат назад не поверне ваші дані.","\u002Fblog\u002Fversion-history-is-not-a-backup\u002Fcard-800x500.png",{"path":1325,"title":1326,"description":1327,"published":1328,"category":869,"image":1329,"draft":772},"\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database","Три способи зробити резервну копію бази Supabase, і чого бракує","Панель, pg_dump і керований сервіс. Що насправді зберігає кожен спосіб, що тихо лишає осторонь і який із них переживе втрату акаунта.","2026-08-09","\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database\u002Fcard-800x500.png",{"path":1331,"title":1332,"description":1333,"published":1328,"category":768,"image":1334,"draft":772},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","Які API-ключі безпечні у браузері, а які ні","Ваш ключ anon у Supabase створений, щоб бути публічним. Ключ service_role для цього не створений, і він ігнорує всі правила, які ви задали. Як їх розрізнити.","\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend\u002Fcard-800x500.png",1794213654200]