[{"data":1,"prerenderedAt":1229},["ShallowReactive",2],{"blog-uk-violates-foreign-key-constraint":3,"blog-index-uk":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_uk\u002Fblog\u002Fviolates-foreign-key-constraint.md","Помилка Supabase \"violates foreign key constraint\" під час вставки",{"type":7,"value":8,"toc":647},"minimark",[9,13,24,31,34,39,50,53,171,180,192,196,199,223,236,249,252,260,267,273,276,280,283,289,297,314,320,343,353,389,393,396,403,409,423,427,441,447,452,456,459,466,472,482,485,491,499,505,509,514,531,534,540,543,547,550,559,581,590,604,608,640],[10,11,12],"p",{},"Ви натискаєте Зберегти на новому профілі у своєму застосунку, і нічого не\nзмінюється. Сторінка не показує жодного повідомлення. У консолі браузера воно є:",[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\" означає, що рядок, який ви намагалися\nзберегти, називає рядок іншої таблиці, а такого рядка не існує. Тут профіль ніс\n",[21,28,29],{},"id",", що мав належати комусь, хто зареєструвався, і Postgres не знайшов нікого з\nтаким id. Нічого не збережено.",[10,32,33],{},"Найшвидше повідомлення зникне, якщо видалити правило. Після цього збереження\nпроходять, а рядки, які вони створюють, вказують у порожнечу: позиція замовлення\nбез замовлення, профіль без жодної людини за ним. Спершу зʼясуйте, якого рядка\nбракує і чому. Він зник одним із небагатьох способів.",[35,36,38],"h2",{"id":37},"що-означає-violates-foreign-key-constraint-у-supabase","Що означає \"violates foreign key constraint\" у Supabase?",[10,40,41,42,49],{},"Це означає, що один стовпець вашого рядка має збігатися з рядком іншої таблиці,\nзначення, яке ви надіслали, не збігається з жодним, і Postgres відхилив увесь\nрядок.\n",[43,44,48],"a",{"href":45,"rel":46},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fddl-constraints.html",[47],"nofollow","Документація Postgres про обмеження","\nвизначає це правило одним реченням: зовнішній ключ \"specifies that the values in\na column (or a group of columns) must match the values appearing in some row of\nanother table.\"",[10,51,52],{},"Уявіть іншу таблицю як вулицю, а кожен її рядок як будинок із номером. Ваш новий\nрядок це лист, а стовпець із зовнішнім ключем це номер будинку, написаний на\nньому. Postgres працює як сортувальний центр. Перш ніж прийняти лист, він шукає\nномер на вулиці, і лист до будинку, якого немає, одразу повертається.\nПовідомлення каже, який лист повернувся. Рядок деталей каже, на якій вулиці\nшукали.",[54,55,56,72],"table",{},[57,58,59],"thead",{},[60,61,62,66,69],"tr",{},[63,64,65],"th",{},"Поле",[63,67,68],{},"Приклад",[63,70,71],{},"Що воно вам каже",[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],{},"Код Postgres для цієї помилки, ",[21,91,92],{},"foreign_key_violation"," у ",[43,95,98],{"href":96,"rel":97},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Futils\u002Ferrcodes.txt",[47],"його списку кодів",[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],{},"Таблиця, у яку ви писали, потім назва правила",[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\"."," або те саме без значення",[78,128,129],{},"Стовпець, значення, яке ви надіслали, і таблиця, у якій шукав Postgres. Наступний розділ пояснює, коли значення пропускається",[60,131,132,137,142],{},[78,133,134],{},[21,135,136],{},"hint",[78,138,139],{},[21,140,141],{},"null",[78,143,144],{},"Для цієї помилки нічого додати",[60,146,147,150,155],{},[78,148,149],{},"HTTP-статус",[78,151,152],{},[21,153,154],{},"409 Conflict",[78,156,157,158,161,162,167,168,170],{},"PostgREST, частина Supabase, що перетворює виклики ",[21,159,160],{},"supabase.from(...)"," на SQL, у ",[43,163,166],{"href":164,"rel":165},"https:\u002F\u002Fgithub.com\u002FPostgREST\u002Fpostgrest\u002Fblob\u002Fmain\u002Fsrc\u002Flibrary\u002FPostgREST\u002FError.hs",[47],"своєму коді"," зіставляє ",[21,169,86],{}," зі статусом 409",[10,172,173,174,179],{},"Формулювання походить із\n",[43,175,178],{"href":176,"rel":177},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Futils\u002Fadt\u002Fri_triggers.c",[47],"файлу Postgres, який виконує ці перевірки",".\nUpdate теж її викликає, коли змінює стовпець на номер без будинку, тому\nповідомлення починається з \"insert or update\".",[10,181,182,183,186,187,191],{},"Повідомлення, що починається з ",[21,184,185],{},"update or delete on table",", це те саме правило,\nпобачене з боку вулиці: ви намагалися знести будинок, на який досі вказують\nлисти. Воно виникає під час видалення, і\n",[43,188,190],{"href":189},"\u002Fblog\u002Fdatabase-error-deleting-user","стаття про видалення користувачів"," його\nрозбирає.",[35,193,195],{"id":194},"який-рядок-шукає-postgres","Який рядок шукає Postgres?",[10,197,198],{},"Той, що в рядку деталей, у таблиці, яку цей рядок називає. Дві речі роблять його\nскладнішим для читання, ніж здається.",[10,200,201,205,206,209,210,214,215,218,219,222],{},[202,203,204],"strong",{},"Іноді значення пропущене."," Postgres пише ",[21,207,208],{},"Key (id)=(…)"," лише тоді, коли той,\nхто викликає, може читати цей стовпець вашої таблиці. Коли на таблиці, у яку ви\nписали, увімкнено Row Level Security,\n",[43,211,213],{"href":176,"rel":212},[47],"його код","\nпропускає значення з коментарем \"we don't return any specifics to avoid leaking\ndata\", і ви отримуєте ",[21,216,217],{},"Key is not present in table \"users\"."," Саме це бачить запит\nіз браузера до захищеної таблиці. Значення те, що надіслав ваш застосунок, тож\nвідкрийте інструменти розробника в браузері, знайдіть на вкладці Мережа запит до\n",[21,220,221],{},"\u002Frest\u002Fv1\u002Fprofiles"," і прочитайте значення в payload.",[10,224,225,226,230,231,235],{},"Якщо ваша консоль показує значення для запиту, який ваш застосунок зробив із\nпублічним ключем, Row Level Security на таблиці, у яку ви писали, вимкнено.\nТаблиця з вимкненими правилами віддає свої рядки незнайомцю так само охоче, як і\nвам, а ",[43,227,229],{"href":228},"\u002Fblog\u002Fenable-rls-on-every-supabase-table","увімкнення правил на кожній таблиці","\nпоказує перемикач і перше правило. Наше\n",[43,232,234],{"href":233},"\u002Fsecurity-scanner","безкоштовне сканування"," читає ваш живий застосунок із\nпублічним ключем, який отримує кожен відвідувач, і перелічує таблиці, що йому\nвідповідають. Обліковий запис не потрібен.",[10,237,238,205,241,244,245,248],{},[202,239,240],{},"Назва таблиці без схеми.",[21,242,243],{},"users",", і в проєкті Supabase\nстовпець, що вказує на людей, майже завжди вказує на ",[21,246,247],{},"auth.users",", таблицю, де\nSupabase Auth тримає всіх зареєстрованих людей. Її немає серед ваших публічних\nтаблиць у Table Editor, тому повідомлення читається так, ніби називає таблицю, якої\nви ніколи не створювали.",[10,250,251],{},"Щоб побачити, куди саме вказує правило, виконайте це в SQL Editor із назвою\nправила з повідомлення:",[14,253,258],{"className":254,"code":256,"language":257,"meta":23},[255],"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,259,256],{"__ignoreMap":23},[10,261,262,263,266],{},"Відповідь назве таблицю, на якій стоїть правило, і що це правило каже, наприклад\n",[21,264,265],{},"FOREIGN KEY (id) REFERENCES auth.users(id) ON DELETE CASCADE",": ваш стовпець,\nпотім таблиця й стовпець, з якими він має збігатися. Потім знайдіть значення:",[14,268,271],{"className":269,"code":270,"language":257,"meta":23},[255],"select id, email, created_at from auth.users where id = '…';\n",[21,272,270],{"__ignoreMap":23},[10,274,275],{},"Якщо не повернеться жодного рядка, людини, яку називає ваш профіль, у цьому\nпроєкті не існує.",[35,277,279],{"id":278},"звідки-береться-рядок-що-вказує-в-порожнечу","Звідки береться рядок, що вказує в порожнечу?",[10,281,282],{},"З небагатьох місць, і таблиця в рядку деталей підкаже, куди дивитися спершу.",[10,284,285,288],{},[202,286,287],{},"Рядок, на який він вказує, так і не зберегли або зберегли другим.","\nЗамовлення та його позиції це два збереження. Якщо ваш застосунок спершу зберігає\nпозиції, або зберігає замовлення, отримує помилку, яку ніколи не читає, і йде далі\nз позиціями, кожна позиція називає замовлення, якого не існує. Збережіть спершу\nрядок, на який вказують, візьміть його id із відповіді й читайте помилку кожного\nкроку перед наступним:",[14,290,295],{"className":291,"code":293,"language":294,"meta":23},[292],"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,296,293],{"__ignoreMap":23},[10,298,299,302,303,308,309,313],{},[21,300,301],{},".select()"," після insert читає новий рядок назад, тож людині потрібне правило, яке\nдозволяє їй читати власні замовлення.\n",[43,304,307],{"href":305,"rel":306},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fsql-createpolicy.html",[47],"Документація Postgres про policies","\nкаже, що новий рядок, який не проходить правил читання, змушує збереження\nзавершитися помилкою, і в Supabase ця помилка звучить як\n",[43,310,312],{"href":311},"\u002Fblog\u002Fnew-row-violates-row-level-security-policy","new row violates row-level security policy",".\nДва збереження це окремі запити, тож якщо позиції не збереглися, замовлення вже\nзбережене: надішліть позиції ще раз або видаліть замовлення.",[315,316],"diagram",{"alt":317,"caption":318,"src":319},"Дві смуги. У першій позицію, адресовану замовленню 14, надсилають раніше, ніж це замовлення існує: на вулиці замовлень там, де мав стояти 14, порожня ділянка, обведена бурштиновим, і смуга закінчується на 23503 і 409. У другій замовлення 14 зберігають першим, і воно світиться бірюзовим, позиція йде слідом із його номером, на вулиці є будинок, і смуга закінчується галочкою.","Позиції, збережені раніше за своє замовлення, вказують на будинок, якого ще немає. Збережіть замовлення, візьміть його id із відповіді, а потім збережіть позиції.","\u002Fblog\u002Fviolates-foreign-key-constraint\u002Fsave-the-order-first-1600x760.png",[10,321,322,325,326,328,329,331,332,334,335,338,339,342],{},[202,323,324],{},"Id прийшов звідкись іще."," Кожен проєкт Supabase має власну ",[21,327,247],{},". Id,\nскопійований із вашого тестового проєкту, id, який конструктор зі ШІ залишив у\nкоді з попереднього перегляду, або рядки, скопійовані з одного проєкту в інший,\nусі називають людей, яких живий проєкт ніколи не бачив. Коли копіюєте дані між\nпроєктами, копіюйте рядки, на які вказують, раніше за рядки, що на них вказують.\nЛюдей за стовпцем, що вказує на ",[21,330,247],{},", так не скопіювати: вони\nреєструються в новому проєкті й отримують при цьому нові id. Профіль, чий ",[21,333,29],{},"\nмає бути id людини, ламається й тоді, коли застосунок заповнює його свіжим\nвипадковим значенням, наприклад ",[21,336,337],{},"crypto.randomUUID()",". Цей номер не належить\nжодному будинку. Беріть ",[21,340,341],{},"user.id"," з поточної сесії.",[10,344,345,348,349,352],{},[202,346,347],{},"Рядок видалили тим часом."," Товар, який прибрали з вашого магазину, поки він\nлежав у чиємусь кошику, або обліковий запис, видалений в іншій вкладці.\nЗбереження, що йде слідом, вказує на будинок, який стояв, коли сторінка\nзавантажилася. Виправлення у вашому застосунку: перевіряйте\n",[21,350,351],{},"error.code === '23503'"," і кажіть людині, що змінилося, наприклад \"Цей товар\nбільше не доступний.\"",[10,354,355,358,359,361,362,365,366,371,372,375,376,379,380,384,385,388],{},[202,356,357],{},"Тригер, що спрацьовує зарано."," Тригер на ",[21,360,247],{},", який копіює кожну нову\nлюдину в ",[21,363,364],{},"profiles",", має спрацьовувати після того, як людину збережено.\n",[43,367,370],{"href":368,"rel":369},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Ftrigger-datachanges.html",[47],"Документація Postgres про тригери","\nкаже, що зміна, яка запускає рядковий тригер ",[21,373,374],{},"BEFORE",", \"is naturally not visible\nto SQL commands executed in a row-level BEFORE trigger, because it hasn't\nhappened yet\". Тож профіль, записаний із тригера ",[21,377,378],{},"before insert",", вказує на\nлюдину, якої ще не існує. Кожна реєстрація падає, і Supabase повідомляє про це як\n",[43,381,383],{"href":382},"\u002Fblog\u002Fdatabase-error-saving-new-user","\"Database error saving new user\"",". Шаблон\nсамого Supabase, який та стаття наводить повністю, використовує ",[21,386,387],{},"after insert",".",[35,390,392],{"id":391},"чому-порожнє-значення-проходить","Чому порожнє значення проходить?",[10,394,395],{},"Бо зовнішній ключ перевіряє лише ті значення, що є. Документація Postgres про\nобмеження каже так: \"Normally, a referencing row need not satisfy the foreign key\nconstraint if any of its referencing columns are null.\" Null це слово Postgres\nдля відсутності будь-якого значення. Лист без номера будинку ніхто не шукає. Він\nпотрапляє в мішок і не належить жодному будинку.",[10,397,398,399,402],{},"Тож позиція замовлення, збережена з порожнім ",[21,400,401],{},"order_id",", приймається і не належить\nжодному замовленню. Ваш застосунок натрапить на неї пізніше, як на позицію, що не\nзʼявляється ні в чиєму кошику. Та сама сторінка називає виправлення: \"declare the\nreferencing column(s) as NOT NULL\".",[14,404,407],{"className":405,"code":406,"language":257,"meta":23},[255],"alter table public.order_items alter column order_id set not null;\n",[21,408,406],{"__ignoreMap":23},[10,410,411,412,416,417,419,420,388],{},"Після цього порожнє значення теж відхиляється, з повідомленням, яке прямо називає\nстовпець,\n",[43,413,415],{"href":414},"\u002Fblog\u002Fnull-value-violates-not-null-constraint","null value in column violates not-null constraint",".\nКоманда не спрацює, поки вже є рядки з порожнім ",[21,418,401],{},", тож спершу\nперелічіть їх через ",[21,421,422],{},"select * from public.order_items where order_id is null",[35,424,426],{"id":425},"чи-може-правило-ховати-рядок-який-він-шукає","Чи може правило ховати рядок, який він шукає?",[10,428,429,430,435,436,440],{},"Ні. Postgres перевіряє зовнішній ключ по кожному рядку іншої таблиці, хоч би що\nказали ваші правила Row Level Security.\n",[43,431,434],{"href":432,"rel":433},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fddl-rowsecurity.html",[47],"Його документація про безпеку на рівні рядків","\nкаже про це прямо: \"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.\" У\n",[43,437,439],{"href":176,"rel":438},[47],"коді, що виконує перевірку",",\nпошук перемикається на власника іншої таблиці, із прапорцем, який не дає\nзастосуватися навіть примусовому правилу.",[10,442,443,444,446],{},"Тож сортувальний центр працює з повним планом вулиці. Ваш застосунок бачить\nвулицю крізь свої правила, які, можливо, показують йому лише ті будинки, куди\nйому можна, а перевірка ніколи не користується цим поглядом. ",[21,445,86],{}," означає, що\nбудинку немає ні для кого. Якщо рядок існує, а ваш застосунок його не бачить,\nзбереження проходить.",[315,448],{"alt":449,"caption":450,"src":451},"Та сама вулиця з будинками, намальована двічі. Ліворуч, якщо дивитися з вікна браузера крізь паркан із навісним замком, світиться лише будинок 12, а решта це бліді пунктирні контури. Праворуч лист, адресований 14, одному з блідих будинків, спускається на ту саму вулицю, де кожен будинок намальований суцільним, і отримує галочку.","Ваш застосунок читає рядки, які дозволяють його правила. Перевірка зовнішнього ключа читає всі рядки, тож рядок, що вказує на будинок, якого ваш застосунок не бачить, приймається.","\u002Fblog\u002Fviolates-foreign-key-constraint\u002Fthe-check-sees-every-house-1600x760.png",[35,453,455],{"id":454},"чи-може-хтось-причепити-рядок-до-чужого-запису","Чи може хтось причепити рядок до чужого запису?",[10,457,458],{},"Так, якщо ваше правило вставки не перевіряє, кому цей запис належить. Перевірка\nтого, що будинок існує, ігнорує ваші правила, тож вона каже так і будинку, який\nналежить комусь іншому.",[10,460,461,462,465],{},"Візьміть ",[21,463,464],{},"order_items"," із таким правилом вставки, яке виглядає безпечним:",[14,467,470],{"className":468,"code":469,"language":257,"meta":23},[255],"create policy \"Add your own order lines\"\non public.order_items for insert\nto authenticated\nwith check (auth.uid() = user_id);\n",[21,471,469],{"__ignoreMap":23},[10,473,474,475,478,479,481],{},"Воно перевіряє, що позиція ваша, і ніколи не дивиться, до чийого замовлення вона\nдодається. Людина, що увійшла в систему й надсилає свій ",[21,476,477],{},"user_id"," разом із чужим\n",[21,480,401],{},", проходить і правило, і зовнішній ключ, і позиція, яку вона написала,\nтепер стоїть у замовленні незнайомця. Та сама форма ставить коментар під\nприватним дописом іншої людини або додає учасника до команди, куди його ніхто не\nзапрошував.",[10,483,484],{},"Замініть його правилом, яке дивиться на замовлення:",[14,486,489],{"className":487,"code":488,"language":257,"meta":23},[255],"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,490,488],{"__ignoreMap":23},[10,492,493,494,498],{},"Видаліть перше правило, коли додаєте це. Postgres поєднує правила одного виду\nчерез \"OR\", як сказано в\n",[43,495,497],{"href":305,"rel":496},[47],"його документації про policies",",\nтож старе й далі пропускало б позицію самостійно.",[10,500,501,502,504],{},"Перевірка ще й відповідає на запитання кожному, кому дозволено вставляти. Речення\nпісля процитованого вище, у документації про безпеку на рівні рядків,\nзастерігає: \"Care must be taken when developing schemas and row level policies to\navoid 'covert channel' leaks of information through such referential integrity\nchecks.\" Збереження, що падає з ",[21,503,86],{},", каже, що будинку немає. Збереження, що\nвдається, каже, що він є, на вулиці, яку людина не може прочитати. Id\nкористувачів у Supabase це довгі випадкові значення, яких ніхто не вгадає. Коли\nстовпець вказує на щось, що можна вгадати, наприклад на адресу електронної пошти\nчи id, що рахується від 1, людина може пробувати значення по одному й дізнатися,\nякі існують.",[35,506,508],{"id":507},"чи-варто-прибрати-зовнішній-ключ","Чи варто прибрати зовнішній ключ?",[10,510,511,512,388],{},"Ні. Без нього збереження проходить, рядок вказує в порожнечу, і відтоді ніщо вам\nне каже, коли це стається. Кошик показує позицію з товаром, який ви прибрали\nминулого місяця, а профіль лишається від людини, якої вже немає в ",[21,513,247],{},[10,515,516,517,520,521,523,524,526,527,530],{},"Правило також вирішує, що стається з цими рядками, коли видаляють рядок, на який\nвони вказують. З ",[21,518,519],{},"on delete cascade"," на правилі від ",[21,522,364],{}," до ",[21,525,247],{},"\nвидалення людини видаляє і її профіль. Без правила профіль лишається з усіма\nособистими даними, які в ньому є.\n",[43,528,529],{"href":189},"Стаття про видалення користувачів"," пояснює,\nяк вирішити, що зберігає кожна таблиця.",[10,532,533],{},"Якщо старі рядки вже вказують у порожнечу, перелічіть їх, перш ніж повертати\nправило чи посилювати його, бо Postgres відмовляється додавати зовнішній ключ,\nпоки хоч один рядок його порушує:",[14,535,538],{"className":536,"code":537,"language":257,"meta":23},[255],"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],{},"Для кожного вирішіть, чи повернеться до нього замовлення, чи він піде. Видалення\nне скасувати без копії бази даних із часу до нього.",[35,544,546],{"id":545},"перш-ніж-лагодити-рядки","Перш ніж лагодити рядки",[10,548,549],{},"Зберігайте копію бази даних із часу, коли ви ще не видалили рядки, що вказують у\nпорожнечу, не скопіювали дані між проєктами й не змінили правило, і дізнавайтеся,\nколи таблиця починає відповідати незнайомцям.",[10,551,552],{},[202,553,554,558],{},[43,555,557],{"href":556},"\u002Fsupabase-backups","Reeve Care"," зберігає копію вашої бази даних Supabase:",[560,561,562,566,569,572,575,578],"ul",{},[563,564,565],"li",{},"зашифрована копія за розкладом, у власному сховищі Reeve в ЄС, поза вашим обліковим записом Supabase",[563,567,568],{},"кожен рядок таким, яким він був до того, як прибирання видалило не ті",[563,570,571],{},"кожна копія перевірена, перш ніж її зараховують, підрахунком рядків у кожній таблиці",[563,573,574],{},"ваші завантажені файли теж, щойно ви підключите облікові дані Storage",[563,576,577],{},"відновлення одним кліком, перед яким береться страхувальна копія поточного стану",[563,579,580],{},"усе, що робить Monitor",[10,582,583],{},[202,584,585,589],{},[43,586,588],{"href":587},"\u002Fwebsite-monitoring","Reeve Monitor"," і далі перевіряє ззовні:",[560,591,592,595,598,601],{},[563,593,594],{},"чи доступний ваш застосунок, з перевіркою щохвилини",[563,596,597],{},"повна перевірка безпеки щогодини, до трьох застосунків",[563,599,600],{},"лист того дня, коли нове сканування оцінить ваш застосунок гірше за попереднє, тож таблиця, на якій вимкнули правила, дійде до вас, і вам не доведеться йти дивитися",[563,602,603],{},"щомісячний звіт простою мовою про те, що він побачив",[35,605,607],{"id":606},"що-зробити-зараз","Що зробити зараз",[609,610,611],"key-takeaways",{},[560,612,613,621,624,627,634,637],{},[563,614,615,616,618,619,388],{},"Прочитайте таблицю в рядку деталей. У Supabase ",[21,617,243],{}," там майже завжди означає ",[21,620,247],{},[563,622,623],{},"Знайдіть значення: у рядку деталей або в запиті, який надіслав ваш застосунок.",[563,625,626],{},"Спершу збережіть рядок, на який вказують, візьміть його id із відповіді й читайте кожну помилку перед наступним збереженням.",[563,628,629,630,633],{},"Дайте ",[21,631,632],{},"not null"," стовпцю, який мусить кудись вказувати, щоб порожнє значення теж відхилялося.",[563,635,636],{},"Нехай кожне правило вставки перевіряє, кому належить рядок, на який вказує новий.",[563,638,639],{},"Залиште зовнішній ключ і перелічіть рядки, що вказують у порожнечу, перш ніж видаляти бодай один.",[10,641,642,643,646],{},"Почніть із таблиці в рядку деталей та id, який надіслав ваш застосунок. Якщо\nвиправлення видаляє рядки або копіює їх між проєктами,\n",[43,644,645],{"href":556},"збережіть копію бази даних"," до того, як його запустите.",{"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":194,"depth":651,"text":195},{"id":278,"depth":651,"text":279},{"id":391,"depth":651,"text":392},{"id":425,"depth":651,"text":426},{"id":454,"depth":651,"text":455},{"id":507,"depth":651,"text":508},{"id":545,"depth":651,"text":546},{"id":606,"depth":651,"text":607},"Основи безпеки","\u002Fblog\u002Fviolates-foreign-key-constraint\u002Fcover-1200x630.png","Вулиця з пронумерованими будинками, одна ділянка порожня й обведена бурштиновим, а попереду лист на відсутній номер.","Supabase \"insert or update on table violates foreign key constraint\": ваш рядок посилається на рядок, якого немає. Який, чому і як це виправити.",false,"md",[667,670,673,676,679],{"q":668,"a":669},"Що означає insert or update on table violates foreign key constraint?","Рядок, який ви намагалися зберегти, вказує на рядок іншої таблиці, якого не існує, і Postgres його відхилив. Нічого не записано. Повідомлення називає таблицю, у яку ви писали, і правило, а рядок деталей називає таблицю, у якій шукав Postgres. Код 23503, і із застосунку на Supabase він приходить як HTTP 409.",{"q":671,"a":672},"Чому помилка згадує таблицю \"users\", хоча в мене такої немає?","Бо йдеться майже напевно про auth.users, таблицю, де Supabase Auth тримає всіх зареєстрованих людей. Postgres пише назву таблиці без її схеми. Ваш стовпець вказує на людину, а id у ньому не належить нікому в цьому проєкті.",{"q":674,"a":675},"Чи може Row Level Security спричинити помилку зовнішнього ключа?","Ні. Postgres перевіряє зовнішній ключ по кожному рядку іншої таблиці, включно з рядками, які ховають ваші правила, тож помилка означає, що рядка справді немає. Той самий факт працює й навпаки: рядок, що вказує на запис, якого людина не бачить, приймається, якщо ваше правило вставки не перевіряє, кому цей запис належить.",{"q":677,"a":678},"Чому рядок деталей каже \"Key is not present\" без значення?","Postgres пропускає значення, коли на таблиці, у яку ви писали, увімкнено Row Level Security, або коли той, хто викликає, не може читати цей стовпець. Значення те, що надіслав ваш застосунок, тож прочитайте його в запиті, на вкладці Мережа в інструментах розробника браузера.",{"q":680,"a":681},"Чи прибрати зовнішній ключ, щоб помилка зникла?","Ні. Збереження проходять, а рядки вказують у порожнечу: позиція замовлення без замовлення, профіль без людини за ним. Правило також вирішує, що стається з цими рядками, коли користувача видаляють. Зʼясуйте, якого рядка бракує, і збережіть його першим, або виправте 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 зовнішній ключ auth.users","supabase зовнішній ключ 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\" під час вставки (код Postgres 23503, HTTP 409 у вашому застосунку) означає, що збережений рядок називає рядок іншої таблиці, якого не існує. Нічого не збережено. Повідомлення називає вашу таблицю й правило, а рядок деталей називає таблицю, у якій шукав Postgres.","Відсутній рядок так і не зберегли, зберегли другим, видалили тим часом або він живе в іншому проєкті. Коли рядок деталей каже \"users\", майже завжди мається на увазі auth.users, де Supabase Auth тримає всіх зареєстрованих людей.","Перевірка читає кожен рядок іншої таблиці, хоч би що казали ваші правила Row Level Security. Тож вона приймає й рядок, що вказує на чужий запис, і ваше правило вставки має перевіряти, кому цей запис належить.","hFIIoX5piu79mBJ-KeQvPRlQKGkfYIkgaj7YErWClNs",[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\": пояснення","Supabase \"max client connections reached\": у пулера немає місця ще для одного клієнта. Ліміт визначає порт у вашому рядку підключення.","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: \"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":692,"title":5,"description":663,"published":693,"category":660,"image":682,"draft":664},{"path":414,"title":716,"description":717,"published":718,"category":660,"image":719,"draft":664},"Помилка Supabase \"null value in column violates not-null constraint\"","Supabase \"null value in column violates not-null constraint\": обовʼязковий стовпець прийшов порожнім. Звідки взявся null і як це виправити.","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!\": куди поділася сесія","\"Auth session missing!\" означає, що supabase-js не знайшла жодної сесії й навіть не спитала Supabase. Куди поділася сесія і як це виправити.","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\": ніхто не ввійшов","\"invalid claim: missing sub claim\": Supabase Auth отримав справжній токен без жодного користувача, ключ проєкту замість сесії.","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\": справжня причина","\"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":739,"title":740,"description":741,"published":742,"category":660,"image":743,"draft":664},"\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":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\" на новому посиланні","На посиланні, надісланому хвилину тому, \"Email link is invalid or has expired\" у Supabase зазвичай означає, що поштовий фільтр використав його першим.","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\": пояснення","Supabase \"canceling statement due to statement timeout\": запит перевищив ліміт своєї ролі, 3 секунди для відвідувача. Спершу перевірте політику RLS.","2026-10-31","\u002Fblog\u002Fcanceling-statement-due-to-statement-timeout\u002Fcard-800x500.png",{"path":189,"title":757,"description":758,"published":759,"category":760,"image":761,"draft":664},"\"Database error deleting user\" у Supabase і що зникає разом","\"Database error deleting user\" у Supabase: ваша база відмовилася видаляти. Причина в логах, а виправлення вирішує, що ще буде видалено.","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\" у 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":769,"title":770,"description":771,"published":772,"category":660,"image":773,"draft":664},"\u002Fblog\u002Fsupabase-invalid-login","\"Invalid login credentials\" у Supabase: три причини","Supabase відповідає \"Invalid login credentials\" на неправильний пароль, невідому пошту й акаунт Google. Як зрозуміти, що саме.","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\": справжня помилка","\"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":781,"title":782,"description":783,"published":784,"category":660,"image":785,"draft":664},"\u002Fblog\u002Fcertificate-not-trusted","Сертифікат не довірений: три причини, крім закінчення строку","Сертифікат не довірений, хоча дата в порядку? Він називає іншу адресу, підписав сам себе або в його ланцюжку бракує ланки. Як зрозуміти, що саме.","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\": що в лапках","Supabase \"invalid input syntax for type uuid\" означає, що застосунок надіслав щось, що не є id. Прочитайте лапки, притримайте запит, залиште колонку.","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\": пояснення","Supabase \"duplicate key value violates unique constraint\": значення вже зайняте. Знайдіть стовпець, оберіть виправлення й сховайте значення від очей.","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\", хоча таблиця на місці","Supabase \"relation does not exist\" або PGRST205: жодна таблиця не має такої назви в цій схемі. Чотири перевірки, і на що зважити, відкриваючи схему.","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\" показує назви аргументів, які надіслав ваш застосунок. Порівняйте їх із функцією.","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\" зазвичай означає, що назва не збігається. Чотири перевірки і рядок для перезавантаження.","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\": два ліміти, один код помилки","Supabase \"Email Rate Limit Exceeded\" обмежує кожен лист вашого проєкту: 2 на годину з вбудованим відправником. Що враховується і як підняти ліміт.","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 причини","Supabase \"Invalid Refresh Token: Refresh Token Not Found\" виводить ваших користувачів із системи. Три причини і як лог Auth їх розрізняє.","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\": три причини","Supabase \"permission denied for schema public\" означає, що бракує GRANT на схему. Що його прибрало і який рядок його повертає.","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\": три речі, які це може означати","Supabase \"Bucket not found\" означає одне з трьох: публічне посилання на приватний бакет, неточну назву або не той проєкт.","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: який ключ перестав працювати?","Помилка Supabase invalid JWT — це PGRST301: підписаний токен не пройшов перевірку. У новому проєкті йдеться про сесію, а не про ключ API.","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\" майже завжди означає нуль","Supabase повертає PGRST116, \"JSON object requested, multiple (or no) rows returned\", коли запит нічого не знайшов. Прихований рядок має вигляд відсутнього.","2026-10-15","\u002Fblog\u002Fsupabase-single-row-error\u002Fcard-800x500.png",{"path":382,"title":853,"description":854,"published":855,"category":660,"image":856,"draft":664},"\"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":858,"title":859,"description":860,"published":861,"category":660,"image":862,"draft":664},"\u002Fblog\u002Fwhat-is-supabase","Що таке Supabase? База даних, яку обрав ваш білдер","Що таке Supabase: база даних, вхід і сховище файлів за вашим застосунком. Supabase тримає сервери, а правила про те, хто читає ваші рядки, ваші.","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","Відкритий у фронтенді ключ AWS це не те саме, що ключ OpenAI","Ключ доступу AWS, відкритий у вашому фронтенді, читає і пише ваше сховище. Рішення зазвичай presigned URL, а не новий ключ.","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\" при вивантаженні","\"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":876,"title":877,"description":878,"published":879,"category":660,"image":880,"draft":664},"\u002Fblog\u002Fbase44-security-scan","Сканування безпеки Base44: те, що бачить лише воно","Сканування безпеки Base44 перевіряє сім видів проблем зсередини. Ось половина, яку читає тільки воно, і половина, на яку воно ніколи не дивиться.","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","Ваш API-ключ витік. Порядок дій","API-ключ витік, і ви не знаєте, що робити першим. Не кожен ключ у вашому фронтенді є витоком, а порядок важить більше за швидкість.","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\" без вимкнення RLS","\"Infinite recursion detected in policy for relation\" означає, що ваша policy запитала таблицю, яку вона захищає. Ось як розірвати коло.","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\" у Supabase, і хибне виправлення","\"No API key found in request\" означає, що запит дійшов до Supabase без ключа. Більшість відповідей натомість вказують на правила вашої бази.","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","Чи безпечний Base44? Дані 5 442 застосунків","Чи безпечний Base44? Ми прогнали дев’ять перевірок на 5 442 робочих застосунках Base44. Майже всі отримали B з причин, яких власник ніколи не обирав.","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: перенесіть його в Supabase Edge Function","Сховати ключ API означає прибрати його з браузера, а Supabase Edge Function це найменше місце, куди його покласти. Два кроки поруч важать більше.","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","Чи відкритий ваш файл .env? Дванадцять шляхів для перевірки","Чи відкритий ваш файл .env на вашому власному вебсервері? Дванадцять адрес скажуть це за хвилину, а влучання означає, що все в ньому вже публічне.","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: усі 1 790 просканованих застосунків v0 отримали A","Безпека v0 на 1 790 живих застосунках v0: усі отримали A. Лише 17 згадували базу даних, і саме це здебільшого вимірює оцінка A.","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","Чи безпечний Bolt? Що показали 1 123 застосунки Bolt","Чи безпечний Bolt? Ми прогнали 1 123 живі застосунки Bolt через дев’ять перевірок. Хостинг був чистим. Знахідки: ключі й відкриті таблиці в самих застосунках.","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","Supabase не працює чи проблема у вашому застосунку? Як розрізнити","Supabase не працює чи зламався ваш застосунок? Пауза, повна база й ваша власна помилка виглядають однаково. Чотири перевірки їх розрізняють.","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","Чи шифрує Supabase мої дані? Так. Ось від чого це захищає","Чи шифрує Supabase дані? Так: AES-256 у стані спокою, TLS під час передавання, SOC 2 та ISO 27001. Що покриває кожне і який витік не зупиняє жодне.","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 і що стається на кожному","Ліміти безкоштовного тарифу Supabase: 500 МБ бази, 5 ГБ egress і жодних резервних копій. Що стається на кожній межі і що змінює Pro за $25.","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","Перевірте резервну копію Supabase до того, як вона знадобиться","Як перевірити резервну копію Supabase: відновити в тестовий проєкт, порахувати рядки, увійти й знайти рядок, якого бракує обрізаному файлу.","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","Безкоштовна резервна копія Supabase через GitHub Action і підступ","Резервна копія Supabase через GitHub Action безкоштовна й годиться багатьом застосункам. Робочий процес, потрібний рядок підключення та egress.","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","Чому ви не можете завантажити резервну копію Supabase","Завантажити резервну копію Supabase на сучасному проєкті не вийде: щоденна копія там фізична. Як це перевірити і як тримати власну копію.","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\": бракує GRANT","З 30 жовтня нова таблиця в Supabase відповідає \"permission denied for table\", доки ви не дасте доступ. Лист показує лише половину виправлення.","\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","Чи є відкритий API-ендпоінт проблемою безпеки? Читайте JSON","Ваше сканування позначило відкритий API-ендпоінт. Чи це важливо, залежить від того, що повернулося, і більшість знайдених належали платформі.","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: чого воно не доведе","Сканування безпеки Lovable: що перевіряють Quick scan і Deep scan, коли запускається кожне, і єдине, чого не доведе жодне сканування зсередини.","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","Чому у вашому дампі Supabase немає користувачів","Запустіть supabase db dump окремо і отримаєте форму вашої бази та жодного її рядка, без схеми auth, у якій живуть ваші користувачі.","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","Домен закінчився, сайт лежить: що буде далі","Ваш домен закінчився і сайт лежить. Ось який годинник уже пішов, чому прострочений сертифікат це менша з двох бід, і як перевірити обидва.","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","Чи безпечний Lovable? Що показали 18 554 застосунки Lovable","Чи безпечний Lovable? Ми прогнали 18 554 живі застосунки Lovable через дев’ять перевірок. Платформа виявилася найчистішою з п’яти. Знахідки були в застосунках.","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","Чек-лист безпеки для vibe coding, у девʼяти перевірках","Чек-лист безпеки для vibe coding із девʼяти пунктів, кожен з яких будь-хто може перевірити у вашому живому застосунку ззовні, і кожен має тест на один рядок.","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","Секретний ключ Stripe у фронтенді може рухати гроші","Секретний ключ Stripe, відкритий у вашому фронтенді, може повертати кошти, списувати й читати кожну картку клієнта. А ключ pk_live_ там і має бути.","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 відкриті: префікс означає опублікувати це","Змінні оточення Vite відкриті у вашому застосунку, бо так просив префікс. VITE_ і NEXT_PUBLIC_ означають опублікувати це, а ШІ, який їх додав, не знав ціни.","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: що позначає скан і що з цього виправляти вам","Безпека Base44 на 5 438 просканованих застосунках: три знахідки майже на кожному належать платформі, source map є бейджем Base44, а ваша частина коротка.","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","Чи безпечний Cursor AI? Редактор, код і опублікований застосунок","Чи безпечний Cursor AI? Три запитання в одному пошуку: що Cursor зберігає, у чому помиляється його код і чи відкритий застосунок, який ви опублікували.","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","Чи безпечний Replit? Що ми знайшли в 3 042 живих застосунках Replit","Чи безпечний Replit? Ми прогнали 3 042 живі застосунки Replit через дев’ять зовнішніх перевірок. Знахідки були в опублікованому застосунку, а не в хостингу.","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: у копії бази даних немає жодного файлу","Supabase storage backup це окрема робота. Копії бази даних зберігають список ваших файлів і жодного з них, тож після відновлення кожне завантаження зламане.","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","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":1049,"title":1050,"description":1051,"published":1052,"category":760,"image":1053,"draft":664},"\u002Fblog\u002Fsupabase-project-paused-recover","Проєкт Supabase на паузі? Ваші дані нікуди не зникли","Supabase поставив ваш проєкт на паузу після тижня без активності. Нічого не видалено, кнопка Restore стоїть поруч із назвою, і у вас є рік, щоб натиснути.","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","Який білдер застосунків з ШІ найбезпечніший? 30 998 тестів","Який білдер застосунків з ШІ найбезпечніший? Ми просканували 30 998 живих застосунків з Lovable, Base44, Replit, v0 і Bolt. Оцінку вирішує не білдер.","2026-09-11","\u002Fblog\u002Fsafest-ai-app-builder\u002Fcard-800x500.png",{"path":228,"title":1061,"description":1062,"published":1063,"category":660,"image":1064,"draft":664},"Увімкніть Row Level Security на кожній таблиці Supabase і перевірте","Row Level Security у Supabase без політики закриває таблицю повністю. Політика без увімкненого перемикача не робить нічого. Ось SQL, і ось перевірка.","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\": чого не бачить попередження","Supabase позначає \"RLS disabled in public\" як помилку. Про політику читання, яка лишає вашу таблицю так само відкритою, він не каже нічого.","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","Як замінити злитий ключ service_role у Supabase","Supabase каже спершу закрити витік. Інші посібники кажуть міняти негайно. Що правильно, залежить від того, куди саме потрапив ваш ключ service_role.","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 у порівнянні, наш теж","Найкращий сканер безпеки для vibe coding залежить від трьох питань, на які не відповідає жоден перелік функцій. Вісім інструментів і ціни.","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: п’ять перевірок, які зробите самі","Supabase security checker читає ваш опублікований застосунок, а не налаштування проєкту. Ось п’ять перевірок і як виконати кожну самостійно.","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: чого не бачить скан URL","Сканер безпеки для vibe coding читає ваш робочий застосунок ззовні. Що це охоплює, чотири речі, яких він не бачить, і як читати результат.","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","Відсутні заголовки безпеки: коли це справді важливо","Відсутні заголовки безпеки трапляються в наших звітах найчастіше. Від чого вони захищають і коли це найменш терміновий рядок вашого звіту.","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","Ваш ключ API OpenAI відкритий у фронтенді. Замініть його.","Ключ API OpenAI, відкритий у фронтенді, не можна прив'язати до домену. Замініть його сьогодні, перенесіть виклик на свій сервер і обмежте витрати.","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 у фронтенді: що насправді віддають 30 998 застосунків","Ключ API, відкритий у вашому фронтенді, зазвичай є ключем Google Maps. Ми просканували 30 998 vibe-coded застосунків і порахували, що витікає насправді.","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, і наш серед них","Чотири види інструментів резервного копіювання Supabase, що копіює кожен і коли безкоштовна GitHub Action виграє в будь-якої оплати, нашої теж.","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","Як користуватися секретами в Replit і що все одно публікується","Як користуватися секретами в Replit: додати, прочитати і усунути дві причини undefined. А ще ключі, які цей інструмент не здатен зберегти в таємниці.","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 без терміналу","На безкоштовному тарифі Supabase резервних копій немає: копію робите ви. Як зробити її з панелі та що втрачає шлях через CSV.","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","Чи безпечний Supabase? Так. Ваш проєкт це інше питання","Чи безпечний Supabase? Платформа проходить аудити, шифрує дані й тримає пентести. Їхня документація каже, де це закінчується і починаються ваші налаштування.","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","Де знайти API-ключі Supabase: anon, service_role і URL","URL проєкту, ключ anon і ключ service_role лежать на одній сторінці панелі. Ось де вона, і яке з чотирьох значень має бути у вашому застосунку.","2026-08-28","\u002Fblog\u002Fwhere-to-find-supabase-api-keys\u002Fcard-800x500.png",{"path":311,"title":1144,"description":1145,"published":1146,"category":660,"image":1147,"draft":664},"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":1149,"title":1150,"description":1151,"published":1152,"category":660,"image":1153,"draft":664},"\u002Fblog\u002Fcors-wildcard-security-risk","Чи є вайлдкард CORS ризиком для безпеки? Майже ніколи.","Чи є вайлдкард CORS ризиком для безпеки? Зазвичай це типове налаштування вашого білдера, і воно не видає нічого, що ваш сервер і так віддавав кожному.","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","Branching у Supabase це не бекап. Він іде тільки вперед.","Чому branching у Supabase це не бекап: гілка стартує без ваших даних, а мердж переносить саму лише схему. Для чого він потрібен і що брати замість нього.","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, і що ламається потім","Як відновити резервну копію Supabase з панелі або з файлу дампа, що саме замінює відновлення і чому ваш застосунок може лишитися зламаним після нього.","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","Ваш бакет Supabase Storage публічний. Чи це проблема?","Публічний бакет Supabase Storage означає, що той, хто має URL файлу, може його відкрити. Він не означає, що хтось може переглянути список вмісту.","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","Чи є проблемою ключ API Google у вашому браузері?","Ключ API Google у відкритому коді сторінки наш сканер знаходить найчастіше, і зазвичай усе гаразд. Вирішує це одне безкоштовне налаштування.","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","ШІ-агент видалив мої дані в Supabase. Що можна відновити?","ШІ-агент видалив дані з вашої бази. Те, що ви зможете відновити, вирішилося раніше, а наступні хвилини вирішують, скільки з цього вціліє.","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","Чи може будь-хто прочитати вашу базу Supabase? 3 680 застосунків","Чи може будь-хто прочитати вашу базу даних Supabase без входу? Ми просканували 30 998 живих застосунків, зроблених ШІ-білдерами, і виміряли, як часто так.","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: ваш застосунок публікує свій вихідний код","Відкрита source map дає будь-кому прочитати оригінальний код вашого застосунку, разом із коментарями. Перевірка за 30 секунд, і що важить насправді.","\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: це той самий anon key?","Supabase замінив anon і service_role публічними та секретними ключами. Який із них має бути у вашому застосунку, а який не має бути ніколи?","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","Чи робить Supabase резервні копії моєї бази? Залежить від тарифу.","Чи робить Supabase резервні копії вашої бази даних? На платних тарифах щодня, на безкоштовному жодної. Як дізнатися, що у вас насправді є.","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 увімкнено. Таблиця досі публічна.","Увімкнути Supabase Row Level Security не означає захистити таблицю. Захищають політики, а та, що полагодила застосунок, часто впускає всіх.","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","Історія версій не є резервною копією. Вона не поверне таблицю.","Lovable і Bolt ведуть історію версій вашого коду. Ваша база даних працює як окремий сервіс, тож відкат назад не поверне ваші дані.","\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","Три способи зробити резервну копію бази Supabase, і чого бракує","Панель, pg_dump і керований сервіс. Що насправді зберігає кожен спосіб, що тихо лишає осторонь і який із них переживе втрату акаунта.","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","Які API-ключі безпечні у браузері, а які ні","Ваш ключ anon у Supabase створений, щоб бути публічним. Ключ service_role для цього не створений, і він ігнорує всі правила, які ви задали. Як їх розрізнити.","\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend\u002Fcard-800x500.png",1794213654136]