[{"data":1,"prerenderedAt":1228},["ShallowReactive",2],{"blog-pt-violates-foreign-key-constraint":3,"blog-index-pt":700},{"id":4,"title":5,"body":6,"category":659,"cover":660,"coverAlt":661,"description":662,"draft":663,"extension":664,"faq":665,"image":681,"keywords":682,"meta":689,"navigation":690,"ogTitle":5,"path":691,"published":692,"seo":693,"stem":694,"tldr":695,"updated":692,"__hash__":699},"blog_pt\u002Fblog\u002Fviolates-foreign-key-constraint.md","Erro do Supabase \"violates foreign key constraint\" ao inserir",{"type":7,"value":8,"toc":646},"minimark",[9,13,24,31,34,39,50,53,170,179,191,195,198,222,235,248,251,259,266,272,275,279,282,288,296,313,319,342,352,388,392,395,402,408,422,426,440,446,451,455,458,465,471,481,484,490,498,504,508,513,530,533,539,542,546,549,558,580,589,603,607,639],[10,11,12],"p",{},"Você clica em Salvar num perfil novo no seu app e nada muda. A página não mostra\nnenhuma mensagem. O console do navegador tem uma:",[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\" significa que a linha que você tentou\nsalvar cita uma linha de outra tabela, e essa linha não existe. Aqui o perfil\nlevava um ",[21,28,29],{},"id"," que deveria pertencer a alguém que se cadastrou, e o Postgres não\nencontrou ninguém com esse id. Nada foi salvo.",[10,32,33],{},"O jeito mais rápido de a mensagem sumir é apagar a regra. Depois disso os\nsalvamentos passam, e as linhas que eles criam apontam para o nada: um item de\npedido sem pedido, um perfil sem ninguém por trás. Descubra primeiro qual linha\nfalta e por quê. Ela sumiu de uma de poucas maneiras.",[35,36,38],"h2",{"id":37},"o-que-significa-violates-foreign-key-constraint-no-supabase","O que significa \"violates foreign key constraint\" no Supabase?",[10,40,41,42,49],{},"Significa que uma coluna da sua linha precisa bater com uma linha de outra\ntabela, o valor que você enviou não bate com nenhuma, e o Postgres recusou a\nlinha inteira. A\n",[43,44,48],"a",{"href":45,"rel":46},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fddl-constraints.html",[47],"nofollow","documentação do Postgres sobre restrições","\ndefine a regra numa frase: uma chave estrangeira \"specifies that the values in a\ncolumn (or a group of columns) must match the values appearing in some row of\nanother table.\"",[10,51,52],{},"Pense na outra tabela como uma rua, e em cada linha dela como uma casa com um\nnúmero. Sua linha nova é uma carta, e a coluna com a chave estrangeira é o número\nda casa escrito nela. O Postgres é o centro de triagem. Antes de aceitar a carta,\nele procura o número na rua, e uma carta para uma casa que não existe volta na\nhora. A mensagem diz qual carta voltou. A linha de detalhe diz em qual rua se\nprocurou.",[54,55,56,72],"table",{},[57,58,59],"thead",{},[60,61,62,66,69],"tr",{},[63,64,65],"th",{},"Campo",[63,67,68],{},"Exemplo",[63,70,71],{},"O que ele diz",[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],{},"O código do Postgres para este erro, chamado ",[21,91,92],{},"foreign_key_violation"," na ",[43,95,98],{"href":96,"rel":97},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Futils\u002Ferrcodes.txt",[47],"lista de códigos dele",[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],{},"A tabela em que você escreveu, e depois o nome da regra",[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\".",", ou o mesmo sem o valor",[78,128,129],{},"A coluna, o valor que você enviou e a tabela onde o Postgres procurou. A próxima seção explica quando o valor fica de fora",[60,131,132,137,142],{},[78,133,134],{},[21,135,136],{},"hint",[78,138,139],{},[21,140,141],{},"null",[78,143,144],{},"Nada a acrescentar para este erro",[60,146,147,150,155],{},[78,148,149],{},"Status HTTP",[78,151,152],{},[21,153,154],{},"409 Conflict",[78,156,157,158,161,162,164,165],{},"O PostgREST, a parte do Supabase que transforma chamadas ",[21,159,160],{},"supabase.from(...)"," em SQL, associa ",[21,163,86],{}," ao status 409 no ",[43,166,169],{"href":167,"rel":168},"https:\u002F\u002Fgithub.com\u002FPostgREST\u002Fpostgrest\u002Fblob\u002Fmain\u002Fsrc\u002Flibrary\u002FPostgREST\u002FError.hs",[47],"código dele",[10,171,172,173,178],{},"O texto vem do\n",[43,174,177],{"href":175,"rel":176},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Futils\u002Fadt\u002Fri_triggers.c",[47],"arquivo do Postgres que roda essas verificações",".\nUm update também o provoca, quando troca a coluna por um número sem casa, e por\nisso a mensagem começa com \"insert or update\".",[10,180,181,182,185,186,190],{},"Uma mensagem que começa com ",[21,183,184],{},"update or delete on table"," é a mesma regra vista do\nlado da rua: você tentou derrubar uma casa para a qual cartas ainda apontam. Essa\nvem de apagar, e\n",[43,187,189],{"href":188},"\u002Fblog\u002Fdatabase-error-deleting-user","o post sobre apagar usuários"," trata dela.",[35,192,194],{"id":193},"qual-linha-o-postgres-está-procurando","Qual linha o Postgres está procurando?",[10,196,197],{},"A da linha de detalhe, na tabela que essa linha cita. Duas coisas a deixam mais\ndifícil de ler do que parece.",[10,199,200,204,205,208,209,213,214,217,218,221],{},[201,202,203],"strong",{},"Às vezes o valor fica de fora."," O Postgres mostra ",[21,206,207],{},"Key (id)=(…)"," só quando\nquem chama pode ler essa coluna da sua tabela. Quando o Row Level Security está\nligado na tabela em que você escreveu,\n",[43,210,212],{"href":175,"rel":211},[47],"o código dele","\nomite o valor, com o comentário \"we don't return any specifics to avoid leaking\ndata\", e você recebe ",[21,215,216],{},"Key is not present in table \"users\"."," É isso que vê uma\nrequisição do navegador para uma tabela protegida. O valor é o que seu app\nenviou, então abra as ferramentas de desenvolvedor do navegador, encontre a\nrequisição para ",[21,219,220],{},"\u002Frest\u002Fv1\u002Fprofiles"," na aba Rede e leia o valor no payload.",[10,223,224,225,229,230,234],{},"Se o seu console mostra o valor numa requisição que seu app fez com a chave\npública, o Row Level Security está desligado na tabela em que você escreveu. Uma\ntabela com as regras desligadas entrega as linhas a um estranho tão facilmente\nquanto a você, e\n",[43,226,228],{"href":227},"\u002Fblog\u002Fenable-rls-on-every-supabase-table","ligar as regras em todas as tabelas","\nmostra o interruptor e a primeira regra. Nossa\n",[43,231,233],{"href":232},"\u002Fsecurity-scanner","varredura gratuita"," lê seu app no ar com a chave pública que\nqualquer visitante recebe e lista as tabelas que respondem a ela. Não precisa de\nconta.",[10,236,237,204,240,243,244,247],{},[201,238,239],{},"O nome da tabela vem sem schema.",[21,241,242],{},"users",", e num projeto\nSupabase uma coluna que aponta para pessoas quase sempre aponta para\n",[21,245,246],{},"auth.users",", a tabela onde o Supabase Auth guarda todas as pessoas cadastradas.\nEla não aparece entre suas tabelas públicas no Table Editor, e por isso a\nmensagem parece citar uma tabela que você nunca criou.",[10,249,250],{},"Para ver exatamente para onde a regra aponta, rode isto no SQL Editor com o nome\nda regra que vem na mensagem:",[14,252,257],{"className":253,"code":255,"language":256,"meta":23},[254],"language-sql","select conrelid::regclass as on_table,\n       pg_get_constraintdef(oid) as rule\nfrom pg_constraint\nwhere conname = 'profiles_id_fkey';\n","sql",[21,258,255],{"__ignoreMap":23},[10,260,261,262,265],{},"A resposta traz a tabela que tem a regra e o que a regra diz, por exemplo\n",[21,263,264],{},"FOREIGN KEY (id) REFERENCES auth.users(id) ON DELETE CASCADE",": sua coluna, e\ndepois a tabela e a coluna com que ela precisa bater. Depois procure o valor:",[14,267,270],{"className":268,"code":269,"language":256,"meta":23},[254],"select id, email, created_at from auth.users where id = '…';\n",[21,271,269],{"__ignoreMap":23},[10,273,274],{},"Se nenhuma linha voltar, a pessoa que seu perfil cita não existe neste projeto.",[35,276,278],{"id":277},"de-onde-vem-uma-linha-que-aponta-para-o-nada","De onde vem uma linha que aponta para o nada?",[10,280,281],{},"De poucos lugares, e a tabela da linha de detalhe diz onde olhar primeiro.",[10,283,284,287],{},[201,285,286],{},"A linha para a qual ela aponta nunca foi salva, ou foi salva em segundo\nlugar."," Um pedido e seus itens são dois salvamentos. Se seu app salva primeiro\nos itens, ou salva o pedido, recebe um erro que nunca lê e segue com os itens,\ncada item cita um pedido que não existe. Salve primeiro a linha para a qual se\naponta, pegue o id dela na resposta e leia o erro de cada passo antes do\npróximo:",[14,289,294],{"className":290,"code":292,"language":293,"meta":23},[291],"language-js","const { data: order, error } = await supabase\n  .from('orders')\n  .insert({ user_id: user.id })\n  .select('id')\n  .single()\n\nif (error) throw error\n\nconst { error: linesError } = await supabase\n  .from('order_items')\n  .insert(cart.map((item) => ({ ...item, order_id: order.id })))\n","js",[21,295,292],{"__ignoreMap":23},[10,297,298,301,302,307,308,312],{},[21,299,300],{},".select()"," depois de um insert lê a linha nova de volta, então a pessoa precisa\nde uma regra que a deixe ler os próprios pedidos. A\n",[43,303,306],{"href":304,"rel":305},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fsql-createpolicy.html",[47],"documentação do Postgres sobre policies","\ndiz que uma linha nova que não passa nas regras de leitura faz o salvamento\nlançar um erro, e no Supabase esse erro é\n",[43,309,311],{"href":310},"\u002Fblog\u002Fnew-row-violates-row-level-security-policy","new row violates row-level security policy",".\nOs dois salvamentos são requisições separadas, então se os itens falharem o\npedido já está salvo: envie os itens de novo ou apague o pedido.",[314,315],"diagram",{"alt":316,"caption":317,"src":318},"Duas faixas. Na primeira, um item endereçado ao pedido 14 é enviado antes de esse pedido existir: a rua dos pedidos tem um terreno vazio contornado em âmbar onde o 14 deveria estar, e a faixa termina em 23503 e 409. Na segunda, o pedido 14 é salvo primeiro e se acende em turquesa, o item vem em seguida com o número dele, a rua tem a casa e a faixa termina com um sinal de visto.","Itens salvos antes do pedido apontam para uma casa que ainda não existe. Salve o pedido, pegue o id dele na resposta e só então salve os itens.","\u002Fblog\u002Fviolates-foreign-key-constraint\u002Fsave-the-order-first-1600x760.png",[10,320,321,324,325,327,328,330,331,333,334,337,338,341],{},[201,322,323],{},"O id veio de outro lugar."," Cada projeto Supabase tem o próprio ",[21,326,246],{},".\nUm id copiado do seu projeto de teste, um que um construtor com IA deixou no\ncódigo a partir de uma prévia, ou linhas copiadas de um projeto para outro citam\ntodos pessoas que o projeto no ar nunca viu. Quando você copiar dados entre\nprojetos, copie as linhas para as quais se aponta antes das linhas que apontam\npara elas. As pessoas por trás de uma coluna que aponta para ",[21,329,246],{}," não se\ncopiam assim: elas se cadastram no projeto novo e recebem ids novos ao fazer\nisso. Um perfil cujo ",[21,332,29],{}," precisa ser o id da pessoa também quebra quando o app\no preenche com um valor aleatório novo, como ",[21,335,336],{},"crypto.randomUUID()",". Esse número\nnão é de casa nenhuma. Use ",[21,339,340],{},"user.id"," da sessão logada.",[10,343,344,347,348,351],{},[201,345,346],{},"A linha foi apagada nesse meio-tempo."," Um produto retirado da sua loja\nenquanto estava no carrinho de alguém, ou uma conta apagada em outra aba. O\nsalvamento seguinte aponta para uma casa que existia quando a página carregou. A\ncorreção fica no seu app: verifique ",[21,349,350],{},"error.code === '23503'"," e diga à pessoa o\nque mudou, por exemplo \"Esse produto não está mais disponível.\"",[10,353,354,357,358,360,361,364,365,370,371,374,375,378,379,383,384,387],{},[201,355,356],{},"Um trigger que roda cedo demais."," Um trigger em ",[21,359,246],{}," que copia cada\npessoa nova para ",[21,362,363],{},"profiles"," precisa rodar depois de a pessoa ser salva. A\n",[43,366,369],{"href":367,"rel":368},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Ftrigger-datachanges.html",[47],"documentação do Postgres sobre triggers","\ndiz que a mudança que dispara um trigger ",[21,372,373],{},"BEFORE"," por linha \"is naturally not\nvisible to SQL commands executed in a row-level BEFORE trigger, because it hasn't\nhappened yet\". Um perfil gravado a partir de um trigger ",[21,376,377],{},"before insert"," aponta,\nportanto, para uma pessoa que ainda não existe. Todo cadastro falha, e o Supabase\no informa como ",[43,380,382],{"href":381},"\u002Fblog\u002Fdatabase-error-saving-new-user","\"Database error saving new user\"",".\nO padrão do próprio Supabase, que esse post mostra inteiro, usa ",[21,385,386],{},"after insert",".",[35,389,391],{"id":390},"por-que-um-valor-vazio-passa","Por que um valor vazio passa?",[10,393,394],{},"Porque uma chave estrangeira só confere os valores que estão lá. A documentação\ndo Postgres sobre restrições diz assim: \"Normally, a referencing row need not\nsatisfy the foreign key constraint if any of its referencing columns are null.\"\nNull é a palavra do Postgres para nenhum valor. Uma carta sem número de casa\nnunca é procurada. Ela vai para o saco e não pertence a casa nenhuma.",[10,396,397,398,401],{},"Então um item de pedido salvo com ",[21,399,400],{},"order_id"," vazio é aceito, e não pertence a\npedido nenhum. Seu app o encontra depois, como um item que não aparece no\ncarrinho de ninguém. A mesma página dá a correção: \"declare the referencing\ncolumn(s) as NOT NULL\".",[14,403,406],{"className":404,"code":405,"language":256,"meta":23},[254],"alter table public.order_items alter column order_id set not null;\n",[21,407,405],{"__ignoreMap":23},[10,409,410,411,415,416,418,419,387],{},"Depois disso um valor vazio também é recusado, com uma mensagem que cita a\ncoluna diretamente,\n",[43,412,414],{"href":413},"\u002Fblog\u002Fnull-value-violates-not-null-constraint","null value in column violates not-null constraint",".\nO comando falha enquanto já existirem linhas com ",[21,417,400],{}," vazio, então liste\nessas linhas antes com ",[21,420,421],{},"select * from public.order_items where order_id is null",[35,423,425],{"id":424},"uma-regra-pode-estar-escondendo-a-linha-procurada","Uma regra pode estar escondendo a linha procurada?",[10,427,428,429,434,435,439],{},"Não. O Postgres confere uma chave estrangeira contra cada linha da outra tabela,\nnão importa o que digam suas regras de Row Level Security.\n",[43,430,433],{"href":431,"rel":432},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fddl-rowsecurity.html",[47],"A documentação dele sobre segurança por linha","\ndiz isso com todas as letras: \"Referential integrity checks, such as unique or\nprimary key constraints and foreign key references, always bypass row security to\nensure that data integrity is maintained.\" No\n",[43,436,438],{"href":175,"rel":437},[47],"código que roda a verificação",",\na busca passa para o dono da outra tabela, com uma flag que impede até uma regra\nforçada de valer.",[10,441,442,443,445],{},"Então o centro de triagem trabalha com a planta completa da rua. Seu app vê a rua\natravés das regras dele, que talvez só mostrem as casas que ele pode visitar, e a\nverificação nunca usa essa visão. Um ",[21,444,86],{}," significa que a casa não existe para\nninguém. Se a linha existe e seu app não consegue vê-la, o salvamento passa.",[314,447],{"alt":448,"caption":449,"src":450},"A mesma rua de casas desenhada duas vezes. À esquerda, vista de uma janela do navegador através de uma cerca com cadeado, só a casa 12 está acesa e as outras são contornos pálidos e tracejados. À direita, uma carta endereçada ao 14, uma das casas pálidas, desce até a mesma rua, onde todas as casas estão desenhadas por inteiro, e recebe um sinal de visto.","Seu app lê as linhas que as regras dele permitem. A verificação da chave estrangeira lê todas as linhas, então uma linha que aponta para uma casa que seu app não vê é aceita.","\u002Fblog\u002Fviolates-foreign-key-constraint\u002Fthe-check-sees-every-house-1600x760.png",[35,452,454],{"id":453},"alguém-pode-pendurar-uma-linha-num-registro-que-não-é-seu","Alguém pode pendurar uma linha num registro que não é seu?",[10,456,457],{},"Sim, a menos que sua regra de inserção confira de quem é esse registro. A\nverificação de que a casa existe ignora suas regras, então ela diz sim para uma\ncasa que é de outra pessoa.",[10,459,460,461,464],{},"Pegue ",[21,462,463],{},"order_items"," com esta regra de inserção, que parece segura:",[14,466,469],{"className":467,"code":468,"language":256,"meta":23},[254],"create policy \"Add your own order lines\"\non public.order_items for insert\nto authenticated\nwith check (auth.uid() = user_id);\n",[21,470,468],{"__ignoreMap":23},[10,472,473,474,477,478,480],{},"Ela confere que o item é seu, e nunca olha a qual pedido ele vai se juntar. Uma\npessoa logada que envia o próprio ",[21,475,476],{},"user_id"," com o ",[21,479,400],{}," de outra pessoa\npassa pela regra e pela chave estrangeira, e um item que ela escreveu agora está\nno pedido de um estranho. A mesma forma põe um comentário embaixo do post privado\nde outra pessoa, ou um membro num time para o qual ninguém o convidou.",[10,482,483],{},"Troque por uma regra que olhe o pedido:",[14,485,488],{"className":486,"code":487,"language":256,"meta":23},[254],"create policy \"Add lines to your own orders\"\non public.order_items for insert\nto authenticated\nwith check (\n  exists (\n    select 1 from public.orders\n    where orders.id = order_items.order_id\n      and orders.user_id = auth.uid()\n  )\n);\n",[21,489,487],{"__ignoreMap":23},[10,491,492,493,497],{},"Apague a primeira regra quando acrescentar esta. O Postgres combina regras do\nmesmo tipo com \"OR\", nas palavras da\n",[43,494,496],{"href":304,"rel":495},[47],"documentação dele sobre policies",",\nentão a antiga continuaria deixando o item passar sozinha.",[10,499,500,501,503],{},"A verificação também responde a uma pergunta para qualquer um que possa inserir.\nA frase que vem depois da citada acima, na documentação sobre segurança por\nlinha, avisa: \"Care must be taken when developing schemas and row level policies\nto avoid 'covert channel' leaks of information through such referential\nintegrity checks.\" Um salvamento que falha com ",[21,502,86],{}," diz que uma casa não está\nlá. Um que dá certo diz que está, numa rua que a pessoa não consegue ler. Os ids\nde usuário do Supabase são valores aleatórios longos que ninguém adivinha. Quando\numa coluna aponta para algo adivinhável, como um endereço de e-mail ou um id que\nconta a partir de 1, uma pessoa pode testar valores um por um e descobrir quais\nexistem.",[35,505,507],{"id":506},"você-deve-remover-a-chave-estrangeira","Você deve remover a chave estrangeira?",[10,509,510,511,387],{},"Não. Sem ela, o salvamento passa, a linha aponta para o nada, e daí em diante\nnada avisa quando isso acontece. Um carrinho mostra um item de um produto que\nvocê retirou no mês passado, e um perfil fica para trás para uma pessoa que não\nestá mais em ",[21,512,246],{},[10,514,515,516,519,520,522,523,525,526,529],{},"A regra também decide o que acontece com essas linhas quando a linha para a qual\nelas apontam é apagada. Com ",[21,517,518],{},"on delete cascade"," na regra que vai de ",[21,521,363],{},"\npara ",[21,524,246],{},", apagar uma pessoa apaga o perfil dela. Sem a regra, o perfil\nfica, com os dados pessoais que tiver.\n",[43,527,528],{"href":188},"O post sobre apagar usuários"," mostra como\nescolher o que cada tabela guarda.",[10,531,532],{},"Se linhas antigas já apontam para o nada, liste-as antes de recolocar uma regra\nou de apertar uma, porque o Postgres se recusa a criar uma chave estrangeira\nenquanto alguma linha a violar:",[14,534,537],{"className":535,"code":536,"language":256,"meta":23},[254],"select i.*\nfrom public.order_items i\nleft join public.orders o on o.id = i.order_id\nwhere i.order_id is not null\n  and o.id is null;\n",[21,538,536],{"__ignoreMap":23},[10,540,541],{},"Decida para cada uma se ela recebe o pedido de volta ou vai embora. Apagá-las\nnão tem volta sem uma cópia do banco de antes.",[35,543,545],{"id":544},"antes-de-consertar-as-linhas","Antes de consertar as linhas",[10,547,548],{},"Guarde uma cópia do banco de antes de apagar linhas que apontam para o nada,\ncopiar dados entre projetos ou mudar uma regra, e fique sabendo quando uma\ntabela começar a responder a estranhos.",[10,550,551],{},[201,552,553,557],{},[43,554,556],{"href":555},"\u002Fsupabase-backups","Reeve Care"," guarda uma cópia do seu banco no Supabase:",[559,560,561,565,568,571,574,577],"ul",{},[562,563,564],"li",{},"uma cópia criptografada num cronograma, no armazenamento próprio da Reeve na UE, fora da sua conta do Supabase",[562,566,567],{},"cada linha como estava antes de uma limpeza apagar as erradas",[562,569,570],{},"cada cópia verificada antes de valer, contando as linhas de cada tabela",[562,572,573],{},"seus arquivos enviados também, assim que você conecta uma credencial do Storage",[562,575,576],{},"uma restauração com um clique, com uma cópia de segurança do estado atual feita antes",[562,578,579],{},"tudo o que o Monitor faz",[10,581,582],{},[201,583,584,588],{},[43,585,587],{"href":586},"\u002Fwebsite-monitoring","Reeve Monitor"," continua conferindo de fora:",[559,590,591,594,597,600],{},[562,592,593],{},"se seu app está no ar, conferido a cada minuto",[562,595,596],{},"a verificação de segurança completa a cada hora, em até três apps",[562,598,599],{},"um e-mail no dia em que uma nova varredura der ao seu app uma nota pior que a anterior, para que uma tabela cujas regras foram desligadas chegue até você sem você ter que ir olhar",[562,601,602],{},"um relatório mensal, em linguagem simples, do que ele viu",[35,604,606],{"id":605},"o-que-fazer-agora","O que fazer agora",[608,609,610],"key-takeaways",{},[559,611,612,620,623,626,633,636],{},[562,613,614,615,617,618,387],{},"Leia a tabela na linha de detalhe. No Supabase, ",[21,616,242],{}," ali quase sempre significa ",[21,619,246],{},[562,621,622],{},"Encontre o valor: na linha de detalhe ou na requisição que seu app enviou.",[562,624,625],{},"Salve primeiro a linha para a qual se aponta, pegue o id dela na resposta e leia cada erro antes do próximo salvamento.",[562,627,628,629,632],{},"Dê ",[21,630,631],{},"not null"," a uma coluna que precisa apontar para algum lugar, para que um valor vazio também seja recusado.",[562,634,635],{},"Faça cada regra de inserção conferir de quem é a linha para a qual a nova aponta.",[562,637,638],{},"Mantenha a chave estrangeira, e liste as linhas que apontam para o nada antes de apagar qualquer uma.",[10,640,641,642,645],{},"Comece pela tabela da linha de detalhe e pelo id que seu app enviou. Se a\ncorreção apaga linhas ou as copia entre projetos,\n",[43,643,644],{"href":555},"guarde uma cópia do banco"," de antes de rodar.",{"title":23,"searchDepth":647,"depth":647,"links":648},3,[649,651,652,653,654,655,656,657,658],{"id":37,"depth":650,"text":38},2,{"id":193,"depth":650,"text":194},{"id":277,"depth":650,"text":278},{"id":390,"depth":650,"text":391},{"id":424,"depth":650,"text":425},{"id":453,"depth":650,"text":454},{"id":506,"depth":650,"text":507},{"id":544,"depth":650,"text":545},{"id":605,"depth":650,"text":606},"Noções de segurança","\u002Fblog\u002Fviolates-foreign-key-constraint\u002Fcover-1200x630.png","Uma rua de casas numeradas, com um terreno vazio contornado em âmbar, e na frente uma carta endereçada ao número que falta.","Supabase \"insert or update on table violates foreign key constraint\": sua linha aponta para uma linha que não existe. Qual, por quê, e como corrigir.",false,"md",[666,669,672,675,678],{"q":667,"a":668},"O que significa insert or update on table violates foreign key constraint?","A linha que você tentou salvar aponta para uma linha de outra tabela que não existe, e o Postgres a recusou. Nada foi gravado. A mensagem cita a tabela em que você escreveu e a regra, e a linha de detalhe cita a tabela onde o Postgres procurou. O código é 23503, e de um app Supabase ele chega como HTTP 409.",{"q":670,"a":671},"Por que o erro fala da tabela \"users\" se eu não tenho nenhuma?","Porque a tabela em questão é quase certamente auth.users, onde o Supabase Auth guarda todas as pessoas cadastradas. O Postgres mostra o nome de uma tabela sem o schema. Sua coluna aponta para uma pessoa, e o id que ela guarda não é de ninguém neste projeto.",{"q":673,"a":674},"O Row Level Security pode causar um erro de chave estrangeira?","Não. O Postgres confere uma chave estrangeira contra cada linha da outra tabela, inclusive as linhas que suas regras escondem, então o erro significa que a linha realmente não está lá. O mesmo fato vale ao contrário: uma linha que aponta para um registro que a pessoa não consegue ver é aceita, a menos que sua regra de inserção confira de quem é esse registro.",{"q":676,"a":677},"Por que a linha de detalhe diz \"Key is not present\" sem o valor?","O Postgres omite o valor quando o Row Level Security está ligado na tabela em que você escreveu, ou quando quem chama não pode ler essa coluna. O valor é o que seu app enviou, então leia na requisição, na aba Rede das ferramentas de desenvolvedor do navegador.",{"q":679,"a":680},"Devo remover a chave estrangeira para o erro parar?","Não. Os salvamentos passam e as linhas apontam para o nada: um item de pedido sem pedido, um perfil sem ninguém por trás. A regra também decide o que acontece com essas linhas quando um usuário é apagado. Descubra qual linha falta e salve-a primeiro, ou corrija o id.","\u002Fblog\u002Fviolates-foreign-key-constraint\u002Fcard-800x500.png",[683,684,685,686,687,688],"violates foreign key constraint supabase","insert or update on table violates foreign key constraint","key is not present in table","postgres 23503","supabase chave estrangeira auth.users","supabase chave estrangeira row level security",{},true,"\u002Fblog\u002Fviolates-foreign-key-constraint","2026-11-07",{"title":5,"description":662},"blog\u002Fviolates-foreign-key-constraint",[696,697,698],"Supabase \"violates foreign key constraint\" ao inserir (código do Postgres 23503, HTTP 409 no seu app) significa que a linha que você salvou cita uma linha de outra tabela que não existe. Nada foi salvo. A mensagem cita a sua tabela e a regra, e a linha de detalhe cita a tabela onde o Postgres procurou.","A linha que falta nunca foi salva, foi salva em segundo lugar, foi apagada nesse meio-tempo ou mora em outro projeto. Quando a linha de detalhe diz \"users\", quase sempre se trata de auth.users, onde o Supabase Auth guarda todas as pessoas cadastradas.","A verificação lê cada linha da outra tabela, não importa o que digam suas regras de Row Level Security. Então ela também aceita uma linha que aponta para o registro de outra pessoa, e sua regra de inserção precisa conferir de quem é esse registro.","BK7lsPna5kql6Nwi2beh96seUV0iT_J4_bephin-Dno",[701,707,713,714,719,725,731,737,743,749,755,761,767,773,779,785,791,797,803,809,815,821,827,833,839,845,851,856,862,868,874,880,886,892,898,904,910,916,922,928,934,940,946,952,958,964,969,975,981,987,993,999,1005,1011,1017,1023,1029,1035,1041,1047,1053,1059,1064,1070,1076,1082,1088,1094,1100,1106,1112,1118,1124,1130,1136,1142,1147,1153,1159,1165,1171,1177,1183,1189,1194,1200,1206,1212,1217,1223],{"path":702,"title":703,"description":704,"published":705,"category":659,"image":706,"draft":663},"\u002Fblog\u002Fmax-client-connections-reached","Supabase \"max client connections reached\", explicado","Supabase \"max client connections reached\": o pooler não tem lugar para mais um cliente. A porta da sua string de conexão define o limite.","2026-11-09","\u002Fblog\u002Fmax-client-connections-reached\u002Fcard-800x500.png",{"path":708,"title":709,"description":710,"published":711,"category":659,"image":712,"draft":663},"\u002Fblog\u002Funsupported-provider-provider-is-not-enabled","Erro do Supabase: \"Unsupported provider: provider is not enabled\"","\"Unsupported provider: provider is not enabled\": o Google está desativado no seu projeto Supabase. Como ativar e corrigir o redirect_uri_mismatch.","2026-11-08","\u002Fblog\u002Funsupported-provider-provider-is-not-enabled\u002Fcard-800x500.png",{"path":691,"title":5,"description":662,"published":692,"category":659,"image":681,"draft":663},{"path":413,"title":715,"description":716,"published":717,"category":659,"image":718,"draft":663},"Erro do Supabase \"null value in column violates not-null constraint\"","Supabase \"null value in column violates not-null constraint\": uma coluna obrigatória chegou vazia. De onde veio o null, e como corrigir.","2026-11-06","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fcard-800x500.png",{"path":720,"title":721,"description":722,"published":723,"category":659,"image":724,"draft":663},"\u002Fblog\u002Fauth-session-missing","Supabase \"Auth session missing!\": para onde foi a sessão","\"Auth session missing!\" é o supabase-js sem achar nenhuma sessão logada e sem chegar a perguntar ao Supabase. Para onde foi a sessão, e a correção.","2026-11-05","\u002Fblog\u002Fauth-session-missing\u002Fcard-800x500.png",{"path":726,"title":727,"description":728,"published":729,"category":659,"image":730,"draft":663},"\u002Fblog\u002Finvalid-claim-missing-sub-claim","Supabase \"invalid claim: missing sub claim\": ninguém está logado","\"invalid claim: missing sub claim\": o Supabase Auth recebeu um token autêntico sem nenhum usuário, a chave do projeto no lugar de uma sessão.","2026-11-04","\u002Fblog\u002Finvalid-claim-missing-sub-claim\u002Fcard-800x500.png",{"path":732,"title":733,"description":734,"published":735,"category":659,"image":736,"draft":663},"\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function","\"Failed to send a request to the Edge Function\": a causa real","\"Failed to send a request to the Edge Function\": o navegador não recebeu uma resposta legível. O console dele diz por quê e se a sua função rodou.","2026-11-03","\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function\u002Fcard-800x500.png",{"path":738,"title":739,"description":740,"published":741,"category":659,"image":742,"draft":663},"\u002Fblog\u002Fsupabase-auth-redirects-to-localhost","Por que o Supabase redireciona links de e-mail para localhost:3000","Se o Supabase redireciona para localhost:3000, a Site URL nunca foi trocada ou o seu endereço falta em Redirect URLs. A solução, e o curinga a evitar.","2026-11-02","\u002Fblog\u002Fsupabase-auth-redirects-to-localhost\u002Fcard-800x500.png",{"path":744,"title":745,"description":746,"published":747,"category":659,"image":748,"draft":663},"\u002Fblog\u002Femail-link-is-invalid-or-has-expired","Supabase \"Email link is invalid or has expired\" num link novo","Num link enviado há um minuto, \"Email link is invalid or has expired\" no Supabase costuma significar que um filtro de e-mail o usou antes. Como resolver.","2026-11-01","\u002Fblog\u002Femail-link-is-invalid-or-has-expired\u002Fcard-800x500.png",{"path":750,"title":751,"description":752,"published":753,"category":659,"image":754,"draft":663},"\u002Fblog\u002Fcanceling-statement-due-to-statement-timeout","Supabase \"canceling statement due to statement timeout\", explicado","Supabase \"canceling statement due to statement timeout\": uma consulta passou do limite do seu papel, 3 segundos para um visitante. Veja antes a política RLS.","2026-10-31","\u002Fblog\u002Fcanceling-statement-due-to-statement-timeout\u002Fcard-800x500.png",{"path":188,"title":756,"description":757,"published":758,"category":759,"image":760,"draft":663},"\"Database error deleting user\" no Supabase, e o que vai junto","\"Database error deleting user\" no Supabase: seu banco recusou a exclusão. O motivo está nos seus logs, e a correção decide o que mais é apagado.","2026-10-30","Backups","\u002Fblog\u002Fdatabase-error-deleting-user\u002Fcard-800x500.png",{"path":762,"title":763,"description":764,"published":765,"category":659,"image":766,"draft":663},"\u002Fblog\u002Fstripe-webhook-no-signatures-found","\"No signatures found matching the expected signature\" no Supabase","O webhook do Stripe numa Edge Function do Supabase diz \"No signatures found matching the expected signature\"? São duas causas. Continue verificando.","2026-10-29","\u002Fblog\u002Fstripe-webhook-no-signatures-found\u002Fcard-800x500.png",{"path":768,"title":769,"description":770,"published":771,"category":659,"image":772,"draft":663},"\u002Fblog\u002Fsupabase-invalid-login","\"Invalid login credentials\" no Supabase: as três causas","O Supabase diz \"Invalid login credentials\" para senha errada, e-mail desconhecido e conta Google. Como saber qual é, e por que é vago.","2026-10-28","\u002Fblog\u002Fsupabase-invalid-login\u002Fcard-800x500.png",{"path":774,"title":775,"description":776,"published":777,"category":659,"image":778,"draft":663},"\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code","\"Edge Function returned a non-2xx status code\": o erro real","\"Edge Function returned a non-2xx status code\" quer dizer que sua função do Supabase respondeu com um erro. Veja onde ler o erro real.","2026-10-27","\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code\u002Fcard-800x500.png",{"path":780,"title":781,"description":782,"published":783,"category":659,"image":784,"draft":663},"\u002Fblog\u002Fcertificate-not-trusted","Certificado não confiável: três causas além do vencimento","Certificado não confiável mesmo com a data certa? Ele cita outro endereço, assinou a si mesmo ou falta um elo na cadeia. Veja como saber qual.","2026-10-26","\u002Fblog\u002Fcertificate-not-trusted\u002Fcard-800x500.png",{"path":786,"title":787,"description":788,"published":789,"category":659,"image":790,"draft":663},"\u002Fblog\u002Finvalid-input-syntax-for-type-uuid","Supabase \"invalid input syntax for type uuid\": leia as aspas","Supabase \"invalid input syntax for type uuid\": seu app enviou algo que não é um id. Leia as aspas, segure a consulta, mantenha a coluna.","2026-10-25","\u002Fblog\u002Finvalid-input-syntax-for-type-uuid\u002Fcard-800x500.png",{"path":792,"title":793,"description":794,"published":795,"category":659,"image":796,"draft":663},"\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint","Supabase \"duplicate key value violates unique constraint\", explicado","Supabase \"duplicate key value violates unique constraint\": o valor já está em uso. Ache a coluna, escolha uma correção e tire o valor da tela.","2026-10-24","\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint\u002Fcard-800x500.png",{"path":798,"title":799,"description":800,"published":801,"category":659,"image":802,"draft":663},"\u002Fblog\u002Fsupabase-relation-does-not-exist","Supabase \"relation does not exist\" e a tabela está lá","Supabase \"relation does not exist\" ou PGRST205: nenhuma tabela tem esse nome nesse schema. Quatro verificações, e o que expor um schema abre.","2026-10-23","\u002Fblog\u002Fsupabase-relation-does-not-exist\u002Fcard-800x500.png",{"path":804,"title":805,"description":806,"published":807,"category":659,"image":808,"draft":663},"\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\" mostra os nomes de argumentos que seu app enviou. Compare-os com a função.","2026-10-22","\u002Fblog\u002Fsupabase-function-not-found\u002Fcard-800x500.png",{"path":810,"title":811,"description":812,"published":813,"category":659,"image":814,"draft":663},"\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\" costuma ser um nome que não bate. Quatro verificações e a linha que recarrega.","2026-10-21","\u002Fblog\u002Fsupabase-schema-cache-column-error\u002Fcard-800x500.png",{"path":816,"title":817,"description":818,"published":819,"category":659,"image":820,"draft":663},"\u002Fblog\u002Fsupabase-email-rate-limit-exceeded","Supabase \"Email Rate Limit Exceeded\": dois limites, um só código","Supabase \"Email Rate Limit Exceeded\" limita cada email que seu projeto envia: 2 por hora no remetente embutido. O que conta e como aumentar.","2026-10-20","\u002Fblog\u002Fsupabase-email-rate-limit-exceeded\u002Fcard-800x500.png",{"path":822,"title":823,"description":824,"published":825,"category":659,"image":826,"draft":663},"\u002Fblog\u002Fsupabase-users-logged-out","Supabase \"Invalid Refresh Token: Refresh Token Not Found\": 3 causas","Supabase \"Invalid Refresh Token: Refresh Token Not Found\" desconecta seus usuários. Três causas, e como o log do Auth separa cada uma.","2026-10-19","\u002Fblog\u002Fsupabase-users-logged-out\u002Fcard-800x500.png",{"path":828,"title":829,"description":830,"published":831,"category":659,"image":832,"draft":663},"\u002Fblog\u002Fpermission-denied-for-schema-public","Supabase \"permission denied for schema public\": três causas","Supabase \"permission denied for schema public\" é um grant que falta no schema. O que o removeu, e a linha que o devolve.","2026-10-18","\u002Fblog\u002Fpermission-denied-for-schema-public\u002Fcard-800x500.png",{"path":834,"title":835,"description":836,"published":837,"category":659,"image":838,"draft":663},"\u002Fblog\u002Fsupabase-bucket-not-found","Supabase \"Bucket not found\": as três coisas que isso significa","Supabase \"Bucket not found\" é uma de três coisas: um link público para um bucket privado, um nome que não bate exatamente ou o projeto errado.","2026-10-17","\u002Fblog\u002Fsupabase-bucket-not-found\u002Fcard-800x500.png",{"path":840,"title":841,"description":842,"published":843,"category":659,"image":844,"draft":663},"\u002Fblog\u002Fsupabase-invalid-jwt","Supabase invalid JWT: qual chave parou de funcionar?","Um erro Supabase invalid JWT é o PGRST301: um token assinado não passou na verificação. Num projeto novo isso é a sessão, não a chave de API.","2026-10-16","\u002Fblog\u002Fsupabase-invalid-jwt\u002Fcard-800x500.png",{"path":846,"title":847,"description":848,"published":849,"category":659,"image":850,"draft":663},"\u002Fblog\u002Fsupabase-single-row-error","Supabase \"multiple (or no) rows returned\" quase sempre é nenhuma","O Supabase devolve PGRST116, \"JSON object requested, multiple (or no) rows returned\", quando a consulta não achou nada. Linha escondida parece ausente.","2026-10-15","\u002Fblog\u002Fsupabase-single-row-error\u002Fcard-800x500.png",{"path":381,"title":852,"description":853,"published":854,"category":659,"image":855,"draft":663},"\"Database error saving new user\" no Supabase: onde olhar","\"Database error saving new user\" quer dizer que algo preso ao seu cadastro no Supabase falhou e tudo foi desfeito. O erro real está nos seus logs.","2026-10-14","\u002Fblog\u002Fdatabase-error-saving-new-user\u002Fcard-800x500.png",{"path":857,"title":858,"description":859,"published":860,"category":659,"image":861,"draft":663},"\u002Fblog\u002Fwhat-is-supabase","O que é Supabase? O banco de dados que seu builder escolheu","O que é Supabase: o banco de dados, o login e o armazenamento de arquivos do seu app. O Supabase cuida dos servidores; as regras das suas linhas são suas.","2026-10-13","\u002Fblog\u002Fwhat-is-supabase\u002Fcard-800x500.png",{"path":863,"title":864,"description":865,"published":866,"category":659,"image":867,"draft":663},"\u002Fblog\u002Faws-access-key-exposed-in-frontend","Uma chave AWS exposta no seu frontend não é como uma chave OpenAI","Uma chave de acesso da AWS exposta no seu frontend lê e grava no seu armazenamento. A solução costuma ser uma presigned URL, não uma chave nova.","2026-10-12","\u002Fblog\u002Faws-access-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":869,"title":870,"description":871,"published":872,"category":659,"image":873,"draft":663},"\u002Fblog\u002Fstorage-upload-violates-row-level-security","\"Row-level security policy for table objects\" ao enviar","\"New row violates row-level security policy for table objects\" quer dizer que o seu envio não tem regra insert. Deixar o bucket público não cria nenhuma.","2026-10-11","\u002Fblog\u002Fstorage-upload-violates-row-level-security\u002Fcard-800x500.png",{"path":875,"title":876,"description":877,"published":878,"category":659,"image":879,"draft":663},"\u002Fblog\u002Fbase44-security-scan","Scan de segurança do Base44: o que só ele enxerga","O scan de segurança do Base44 verifica sete tipos de problema por dentro. Esta é a metade que ele lê e mais ninguém, e a metade que ele nunca olha.","2026-10-10","\u002Fblog\u002Fbase44-security-scan\u002Fcard-800x500.png",{"path":881,"title":882,"description":883,"published":884,"category":659,"image":885,"draft":663},"\u002Fblog\u002Fapi-key-leaked-what-to-do","Sua chave de API vazou. A ordem em que agir","Uma chave de API vazou e você não sabe o que fazer primeiro. Nem toda chave no seu frontend é um vazamento, e a ordem importa mais que a pressa.","2026-10-09","\u002Fblog\u002Fapi-key-leaked-what-to-do\u002Fcard-800x500.png",{"path":887,"title":888,"description":889,"published":890,"category":659,"image":891,"draft":663},"\u002Fblog\u002Finfinite-recursion-in-policy-for-relation","\"Infinite recursion detected in policy\" sem desligar o RLS","\"Infinite recursion detected in policy for relation\" significa que a sua policy perguntou à tabela que ela protege. Veja como quebrar o círculo.","2026-10-08","\u002Fblog\u002Finfinite-recursion-in-policy-for-relation\u002Fcard-800x500.png",{"path":893,"title":894,"description":895,"published":896,"category":659,"image":897,"draft":663},"\u002Fblog\u002Fno-api-key-found-in-request","\"No API key found in request\" no Supabase, e a correção errada","\"No API key found in request\" significa que sua requisição chegou ao Supabase sem chave. Quase toda resposta aponta para as regras do seu banco.","2026-10-07","\u002Fblog\u002Fno-api-key-found-in-request\u002Fcard-800x500.png",{"path":899,"title":900,"description":901,"published":902,"category":659,"image":903,"draft":663},"\u002Fblog\u002Fis-base44-safe","O Base44 é seguro? O que vimos em 5.442 apps Base44","O Base44 é seguro? Rodamos nove verificações em 5.442 apps Base44 no ar. Quase todas tiraram B por motivos que o dono nunca escolheu.","2026-10-06","\u002Fblog\u002Fis-base44-safe\u002Fcard-800x500.png",{"path":905,"title":906,"description":907,"published":908,"category":659,"image":909,"draft":663},"\u002Fblog\u002Fmove-a-secret-to-an-edge-function","Esconder uma chave de API: mova para uma Edge Function do Supabase","Esconder uma chave de API é tirá-la do navegador, e uma Edge Function do Supabase é o menor lugar para guardá-la. Dois passos em volta pesam mais.","2026-10-05","\u002Fblog\u002Fmove-a-secret-to-an-edge-function\u002Fcard-800x500.png",{"path":911,"title":912,"description":913,"published":914,"category":659,"image":915,"draft":663},"\u002Fblog\u002Fenv-file-exposed-on-your-server","Seu arquivo .env está exposto? Os doze caminhos para testar","Seu arquivo .env está exposto no seu próprio servidor web? Doze endereços dizem isso em um minuto, e um acerto significa que tudo nele já é público.","2026-10-04","\u002Fblog\u002Fenv-file-exposed-on-your-server\u002Fcard-800x500.png",{"path":917,"title":918,"description":919,"published":920,"category":659,"image":921,"draft":663},"\u002Fblog\u002Fis-v0-safe","Segurança do v0: os 1.790 aplicativos v0 escaneados tiraram A","A segurança do v0 medida em 1.790 aplicativos v0 no ar: todos tiraram A. Só 17 citavam um banco de dados, e é sobretudo isso que esse A mede.","2026-10-03","\u002Fblog\u002Fis-v0-safe\u002Fcard-800x500.png",{"path":923,"title":924,"description":925,"published":926,"category":659,"image":927,"draft":663},"\u002Fblog\u002Fis-bolt-safe","O Bolt é seguro? O que vimos em 1.123 aplicativos Bolt","O Bolt é seguro? Passamos 1.123 aplicativos Bolt no ar por nove verificações. A hospedagem saiu limpa. Os achados: chaves e tabelas abertas nos aplicativos.","2026-10-02","\u002Fblog\u002Fis-bolt-safe\u002Fcard-800x500.png",{"path":929,"title":930,"description":931,"published":932,"category":659,"image":933,"draft":663},"\u002Fblog\u002Fis-supabase-down-or-your-app","O Supabase caiu ou o problema é o seu app? Como saber","O Supabase caiu ou o problema é o seu app? Um projeto pausado, um banco cheio e um erro seu parecem iguais. Quatro verificações separam os casos.","2026-10-01","\u002Fblog\u002Fis-supabase-down-or-your-app\u002Fcard-800x500.png",{"path":935,"title":936,"description":937,"published":938,"category":659,"image":939,"draft":663},"\u002Fblog\u002Fdoes-supabase-encrypt-my-data","O Supabase criptografa meus dados? Sim. Veja o que isso impede","O Supabase criptografa os dados? Sim: AES-256 em repouso, TLS em trânsito, SOC 2 e ISO 27001. O que cada um cobre, e o vazamento que nenhum deles impede.","2026-09-30","\u002Fblog\u002Fdoes-supabase-encrypt-my-data\u002Fcard-800x500.png",{"path":941,"title":942,"description":943,"published":944,"category":659,"image":945,"draft":663},"\u002Fblog\u002Fsupabase-free-plan-limits","O Supabase é gratuito? Sim, com estes limites e sem backups","Sim: o plano gratuito do Supabase vai até 500 MB de banco e 5 GB de egress, sem nenhum backup. O que para em cada limite e o que muda no Pro de $25.","2026-09-29","\u002Fblog\u002Fsupabase-free-plan-limits\u002Fcard-800x500.png",{"path":947,"title":948,"description":949,"published":950,"category":759,"image":951,"draft":663},"\u002Fblog\u002Ftest-your-supabase-backup","Teste o backup do seu Supabase antes do dia em que precisar","Como testar o backup do seu Supabase: restaurar num projeto de teste, contar as linhas, entrar e procurar a linha que falta num arquivo cortado.","2026-09-28","\u002Fblog\u002Ftest-your-supabase-backup\u002Fcard-800x500.png",{"path":953,"title":954,"description":955,"published":956,"category":759,"image":957,"draft":663},"\u002Fblog\u002Fsupabase-backup-github-action","Backup grátis do Supabase com uma GitHub Action, e a pegadinha","Um backup do Supabase com GitHub Action é grátis e serve para muitos apps. O workflow, a connection string certa e o egress de cada execução.","2026-09-27","\u002Fblog\u002Fsupabase-backup-github-action\u002Fcard-800x500.png",{"path":959,"title":960,"description":961,"published":962,"category":759,"image":963,"draft":663},"\u002Fblog\u002Fdownload-your-supabase-backup","Por que você não consegue baixar seu backup do Supabase","Você não consegue baixar seu backup do Supabase num projeto atual, porque a cópia diária é um snapshot físico. Como conferir e como ter uma cópia só sua.","2026-09-26","\u002Fblog\u002Fdownload-your-supabase-backup\u002Fcard-800x500.png",{"path":965,"title":966,"description":967,"published":962,"category":659,"image":968,"draft":663},"\u002Fblog\u002Fsupabase-permission-denied-for-table","Supabase \"permission denied for table\": o grant que falta","A partir de 30 de outubro, uma tabela nova no Supabase responde \"permission denied for table\" até receber um grant. O e-mail mostra só metade.","\u002Fblog\u002Fsupabase-permission-denied-for-table\u002Fcard-800x500.png",{"path":970,"title":971,"description":972,"published":973,"category":659,"image":974,"draft":663},"\u002Fblog\u002Fopen-api-endpoint-exposed","Um endpoint de API aberto é um problema? Leia o JSON","Seu scan apontou um endpoint de API aberto. Se isso importa depende do que voltou, e a maioria dos que encontramos era da própria plataforma.","2026-09-25","\u002Fblog\u002Fopen-api-endpoint-exposed\u002Fcard-800x500.png",{"path":976,"title":977,"description":978,"published":979,"category":659,"image":980,"draft":663},"\u002Fblog\u002Flovable-security-scan","Escaneamento de segurança do Lovable: o que ele não prova","Escaneamento de segurança do Lovable: o que o Quick scan e o Deep scan checam, quando cada um roda, e a única coisa que nenhum escaneamento por dentro prova.","2026-09-24","\u002Fblog\u002Flovable-security-scan\u002Fcard-800x500.png",{"path":982,"title":983,"description":984,"published":985,"category":759,"image":986,"draft":663},"\u002Fblog\u002Fsupabase-backup-auth-users","Por que seu dump do Supabase não tem usuários dentro","Rode supabase db dump sozinho e você recebe a forma do seu banco e nenhuma das linhas dele, sem o schema auth onde seus usuários moram.","2026-09-23","\u002Fblog\u002Fsupabase-backup-auth-users\u002Fcard-800x500.png",{"path":988,"title":989,"description":990,"published":991,"category":659,"image":992,"draft":663},"\u002Fblog\u002Fdomain-and-certificate-expiry","Domínio expirado, site fora do ar: o que acontece agora","Seu domínio expirou e seu site está fora do ar. O relógio que começa a correr, por que o certificado é o problema menor, e como conferir os dois.","2026-09-22","\u002Fblog\u002Fdomain-and-certificate-expiry\u002Fcard-800x500.png",{"path":994,"title":995,"description":996,"published":997,"category":659,"image":998,"draft":663},"\u002Fblog\u002Fis-lovable-safe","O Lovable é seguro? O que vimos em 18.554 aplicativos Lovable","O Lovable é seguro? Passamos 18.554 aplicativos Lovable no ar por nove verificações. A plataforma foi a mais limpa de cinco. Os achados estavam nos aplicativos.","2026-09-21","\u002Fblog\u002Fis-lovable-safe\u002Fcard-800x500.png",{"path":1000,"title":1001,"description":1002,"published":1003,"category":659,"image":1004,"draft":663},"\u002Fblog\u002Fvibe-coded-app-security-checklist","O checklist de segurança do vibe coding, em nove verificações","Um checklist de segurança do vibe coding com nove itens, cada um verificável no seu app de fora por qualquer pessoa, e cada um com um teste de uma linha.","2026-09-20","\u002Fblog\u002Fvibe-coded-app-security-checklist\u002Fcard-800x500.png",{"path":1006,"title":1007,"description":1008,"published":1009,"category":659,"image":1010,"draft":663},"\u002Fblog\u002Fstripe-secret-key-in-frontend","Uma chave secreta da Stripe no frontend pode mover dinheiro","Uma chave secreta da Stripe exposta no seu frontend pode reembolsar, cobrar e ler cada ficha de cliente que você tem. Sua chave pk_live_ é que deve estar aí.","2026-09-19","\u002Fblog\u002Fstripe-secret-key-in-frontend\u002Fcard-800x500.png",{"path":1012,"title":1013,"description":1014,"published":1015,"category":659,"image":1016,"draft":663},"\u002Fblog\u002Fvite-and-next-public-env-vars","Variáveis de ambiente do Vite expostas: o prefixo diz publique isto","Variáveis de ambiente do Vite expostas no seu app: fizeram o que o prefixo pediu. VITE_ e NEXT_PUBLIC_ significam publique isto, e a IA nunca soube o custo.","2026-09-18","\u002Fblog\u002Fvite-and-next-public-env-vars\u002Fcard-800x500.png",{"path":1018,"title":1019,"description":1020,"published":1021,"category":659,"image":1022,"draft":663},"\u002Fblog\u002Fbase44-source-maps","Segurança no Base44: o que um scan aponta e o que é seu para corrigir","Segurança no Base44 em 5.438 apps escaneados: três achados em quase todos são da plataforma, o source map é o selo do Base44, e a sua parte é uma lista curta.","2026-09-17","\u002Fblog\u002Fbase44-source-maps\u002Fcard-800x500.png",{"path":1024,"title":1025,"description":1026,"published":1027,"category":659,"image":1028,"draft":663},"\u002Fblog\u002Fis-cursor-ai-safe","O Cursor AI é seguro? O editor, o código e o app que você publicou","O Cursor AI é seguro? Três perguntas em uma busca: o que o Cursor guarda, o que o código que ele escreve erra, e se o app que você publicou está aberto.","2026-09-16","\u002Fblog\u002Fis-cursor-ai-safe\u002Fcard-800x500.png",{"path":1030,"title":1031,"description":1032,"published":1033,"category":659,"image":1034,"draft":663},"\u002Fblog\u002Fis-replit-safe","O Replit é seguro? O que vimos em 3.042 aplicativos Replit no ar","O Replit é seguro? Passamos 3.042 aplicativos Replit no ar por nove verificações externas. Os achados estavam no aplicativo publicado, não no host.","2026-09-15","\u002Fblog\u002Fis-replit-safe\u002Fcard-800x500.png",{"path":1036,"title":1037,"description":1038,"published":1039,"category":759,"image":1040,"draft":663},"\u002Fblog\u002Fsupabase-storage-backup","Supabase storage backup: sua cópia do banco não tem nenhum arquivo","Um Supabase storage backup é uma tarefa à parte. Os backups do banco guardam a lista dos seus arquivos e nenhum deles, então cada upload fica quebrado.","2026-09-14","\u002Fblog\u002Fsupabase-storage-backup\u002Fcard-800x500.png",{"path":1042,"title":1043,"description":1044,"published":1045,"category":759,"image":1046,"draft":663},"\u002Fblog\u002Fsupabase-point-in-time-recovery","Point-in-time recovery do Supabase: preço e limites","O point-in-time recovery do Supabase rebobina seu banco para qualquer segundo da última semana. Custa $100 por mês além do Pro e cobre só o banco.","2026-09-13","\u002Fblog\u002Fsupabase-point-in-time-recovery\u002Fcard-800x500.png",{"path":1048,"title":1049,"description":1050,"published":1051,"category":759,"image":1052,"draft":663},"\u002Fblog\u002Fsupabase-project-paused-recover","Projeto Supabase pausado? Seus dados continuam lá","O Supabase pausou seu projeto depois de uma semana sem atividade. Nada foi apagado, Restaurar fica ao lado do nome e você tem um ano antes disso mudar.","2026-09-12","\u002Fblog\u002Fsupabase-project-paused-recover\u002Fcard-800x500.png",{"path":1054,"title":1055,"description":1056,"published":1057,"category":659,"image":1058,"draft":663},"\u002Fblog\u002Fsafest-ai-app-builder","Qual builder de apps com IA é mais seguro? 30.998 apps testados","Qual builder de apps com IA é mais seguro? Escaneamos 30.998 apps no ar do Lovable, Base44, Replit, v0 e Bolt. O builder não é o que decide sua nota.","2026-09-11","\u002Fblog\u002Fsafest-ai-app-builder\u002Fcard-800x500.png",{"path":227,"title":1060,"description":1061,"published":1062,"category":659,"image":1063,"draft":663},"Ative Row Level Security em toda tabela do Supabase e comprove","Ativar Row Level Security no Supabase sem política fecha a tabela por completo. Uma política sem a chave ligada não faz nada. Aqui está o SQL, e o teste.","2026-09-10","\u002Fblog\u002Fenable-rls-on-every-supabase-table\u002Fcard-800x500.png",{"path":1065,"title":1066,"description":1067,"published":1068,"category":659,"image":1069,"draft":663},"\u002Fblog\u002Fsupabase-rls-disabled-in-public","Supabase \"RLS disabled in public\": o que o aviso não vê","O Supabase marca \"RLS disabled in public\" como erro. Ele não diz nada sobre a política de leitura que deixa a sua tabela igualmente aberta.","2026-09-09","\u002Fblog\u002Fsupabase-rls-disabled-in-public\u002Fcard-800x500.png",{"path":1071,"title":1072,"description":1073,"published":1074,"category":659,"image":1075,"draft":663},"\u002Fblog\u002Frotate-supabase-service-role-key","Como rotacionar uma chave service_role do Supabase vazada","O Supabase manda fechar o vazamento primeiro. Outros guias mandam rotacionar agora. O que vale depende de para onde a sua chave service_role vazou.","2026-09-08","\u002Fblog\u002Frotate-supabase-service-role-key\u002Fcard-800x500.png",{"path":1077,"title":1078,"description":1079,"published":1080,"category":659,"image":1081,"draft":663},"\u002Fblog\u002Fvibe-coding-security-scanners-compared","Scanners de segurança para vibe coding comparados, o nosso incluído","O melhor scanner de segurança para vibe coding depende de três perguntas que nenhuma lista de recursos responde. Oito ferramentas comparadas, com preços.","2026-09-07","\u002Fblog\u002Fvibe-coding-security-scanners-compared\u002Fcard-800x500.png",{"path":1083,"title":1084,"description":1085,"published":1086,"category":659,"image":1087,"draft":663},"\u002Fblog\u002Fsupabase-security-checker","Supabase security checker: faça você mesmo as cinco verificações","Um Supabase security checker lê o seu app publicado em vez das configurações do projeto. Aqui estão as cinco verificações e como fazer cada uma sozinho.","2026-09-06","\u002Fblog\u002Fsupabase-security-checker\u002Fcard-800x500.png",{"path":1089,"title":1090,"description":1091,"published":1092,"category":659,"image":1093,"draft":663},"\u002Fblog\u002Fvibe-coding-security-scanner","Scanner de segurança para vibe coding: o que ele não vê","Um scanner de segurança para vibe coding lê seu app no ar, de fora. O que isso cobre, as quatro coisas que ele não consegue ver e como ler o resultado.","2026-09-05","\u002Fblog\u002Fvibe-coding-security-scanner\u002Fcard-800x500.png",{"path":1095,"title":1096,"description":1097,"published":1098,"category":659,"image":1099,"draft":663},"\u002Fblog\u002Fmissing-security-headers","Cabeçalhos de segurança faltando: quando isso importa","Cabeçalhos de segurança faltando é o achado que nosso scanner mais imprime. Contra o que eles protegem e quando são a linha menos urgente do relatório.","2026-09-04","\u002Fblog\u002Fmissing-security-headers\u002Fcard-800x500.png",{"path":1101,"title":1102,"description":1103,"published":1104,"category":659,"image":1105,"draft":663},"\u002Fblog\u002Fopenai-api-key-exposed-in-frontend","Sua chave de API da OpenAI está exposta no frontend. Troque-a.","Uma chave de API da OpenAI exposta no frontend não pode ser presa a um domínio. Troque-a hoje, mova a chamada para o seu servidor e limite o gasto.","2026-09-03","\u002Fblog\u002Fopenai-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":1107,"title":1108,"description":1109,"published":1110,"category":659,"image":1111,"draft":663},"\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps","Chave de API exposta no frontend: o que 30.998 apps entregam","Uma chave de API exposta no seu frontend costuma ser uma chave do Google Maps. Escaneamos 30.998 apps vibe-coded e contamos quais segredos vazam de verdade.","2026-09-02","\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps\u002Fcard-800x500.png",{"path":1113,"title":1114,"description":1115,"published":1116,"category":759,"image":1117,"draft":663},"\u002Fblog\u002Fsupabase-backup-tools-compared","Ferramentas de backup do Supabase comparadas, a nossa incluída","Quatro tipos de ferramenta de backup do Supabase, o que cada uma copia de verdade e quando uma GitHub Action gratuita ganha de pagar, nós inclusive.","2026-09-01","\u002Fblog\u002Fsupabase-backup-tools-compared\u002Fcard-800x500.png",{"path":1119,"title":1120,"description":1121,"published":1122,"category":659,"image":1123,"draft":663},"\u002Fblog\u002Freplit-secrets-explained","Como usar secrets no Replit, e o que ainda assim é publicado","Como usar secrets no Replit: adicionar um, ler de volta e resolver as duas causas do undefined. E as chaves que a ferramenta Secrets não consegue proteger.","2026-08-31","\u002Fblog\u002Freplit-secrets-explained\u002Fcard-800x500.png",{"path":1125,"title":1126,"description":1127,"published":1128,"category":759,"image":1129,"draft":663},"\u002Fblog\u002Fback-up-supabase-free-tier","Backups do plano gratuito do Supabase: como fazer sem terminal","No plano gratuito do Supabase não existe backup nenhum: a cópia é você que faz. Como fazer uma pelo painel e o que a rota do CSV deixa para trás.","2026-08-30","\u002Fblog\u002Fback-up-supabase-free-tier\u002Fcard-800x500.png",{"path":1131,"title":1132,"description":1133,"published":1134,"category":659,"image":1135,"draft":663},"\u002Fblog\u002Fis-supabase-secure","O Supabase é seguro? É. Seu projeto é outra pergunta","O Supabase é seguro? A plataforma é auditada, criptografada e passa por pentests. A documentação deles diz onde isso termina e começam suas configurações.","2026-08-29","\u002Fblog\u002Fis-supabase-secure\u002Fcard-800x500.png",{"path":1137,"title":1138,"description":1139,"published":1140,"category":659,"image":1141,"draft":663},"\u002Fblog\u002Fwhere-to-find-supabase-api-keys","Onde ficam as chaves de API do Supabase: anon, service_role e URL","A URL do projeto, a chave anon e a chave service_role ficam todas numa página do painel. Aqui está onde ela fica, e qual dos quatro valores vai no seu app.","2026-08-28","\u002Fblog\u002Fwhere-to-find-supabase-api-keys\u002Fcard-800x500.png",{"path":310,"title":1143,"description":1144,"published":1145,"category":659,"image":1146,"draft":663},"New row violates row-level security policy no Supabase. E agora?","\"New row violates row-level security policy\" significa que o Supabase recusou uma escrita. O conserto rápido reabre a tabela para qualquer um.","2026-08-27","\u002Fblog\u002Fnew-row-violates-row-level-security-policy\u002Fcard-800x500.png",{"path":1148,"title":1149,"description":1150,"published":1151,"category":659,"image":1152,"draft":663},"\u002Fblog\u002Fcors-wildcard-security-risk","Um curinga CORS é um risco de segurança? Quase nunca.","Um curinga CORS é um risco de segurança? Em geral é o padrão do seu builder e não entrega nada que o seu servidor já não desse a qualquer um que pedisse.","2026-08-26","\u002Fblog\u002Fcors-wildcard-security-risk\u002Fcard-800x500.png",{"path":1154,"title":1155,"description":1156,"published":1157,"category":759,"image":1158,"draft":663},"\u002Fblog\u002Fsupabase-branching-is-not-a-backup","Branching do Supabase não é backup. Ele só anda para a frente.","Por que o branching do Supabase não é backup: um branch começa sem os seus dados e um merge só leva o esquema. Para que ele serve e o que usar no lugar.","2026-08-25","\u002Fblog\u002Fsupabase-branching-is-not-a-backup\u002Fcard-800x500.png",{"path":1160,"title":1161,"description":1162,"published":1163,"category":759,"image":1164,"draft":663},"\u002Fblog\u002Fhow-to-restore-a-supabase-backup","Como restaurar um backup do Supabase, e o que quebra depois","Como restaurar um backup do Supabase pelo painel ou por um arquivo de dump, o que a restauração substitui e por que o seu app pode continuar quebrado no fim.","2026-08-24","\u002Fblog\u002Fhow-to-restore-a-supabase-backup\u002Fcard-800x500.png",{"path":1166,"title":1167,"description":1168,"published":1169,"category":659,"image":1170,"draft":663},"\u002Fblog\u002Fsupabase-storage-bucket-public","Seu bucket do Supabase Storage é público. Isso é um problema?","Um bucket público do Supabase Storage significa que quem tem a URL de um arquivo pode abri-lo. Não significa que alguém possa listar o que está lá dentro.","2026-08-23","\u002Fblog\u002Fsupabase-storage-bucket-public\u002Fcard-800x500.png",{"path":1172,"title":1173,"description":1174,"published":1175,"category":659,"image":1176,"draft":663},"\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend","Uma chave de API do Google no frontend é um problema?","Uma chave de API do Google exposta no seu frontend é a chave que nosso scanner mais encontra, e quase sempre está tudo bem. Um ajuste gratuito decide isso.","2026-08-22","\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":1178,"title":1179,"description":1180,"published":1181,"category":759,"image":1182,"draft":663},"\u002Fblog\u002Fai-agent-deleted-my-database","Um agente de IA apagou meus dados do Supabase. O que eu recupero?","Um agente de IA apagou dados do seu banco. O que você recupera já estava decidido antes de ele rodar, e os próximos minutos decidem o resto.","2026-08-21","\u002Fblog\u002Fai-agent-deleted-my-database\u002Fcard-800x500.png",{"path":1184,"title":1185,"description":1186,"published":1187,"category":659,"image":1188,"draft":663},"\u002Fblog\u002Fcan-anyone-read-your-supabase-database","Qualquer um consegue ler seu banco Supabase? Testamos 3.680 apps","Qualquer um consegue ler seu banco de dados Supabase sem fazer login? Escaneamos 30.998 apps no ar criadas com builders de IA e medimos quantas dizem que sim.","2026-08-18","\u002Fblog\u002Fcan-anyone-read-your-supabase-database\u002Fcard-800x500.png",{"path":1190,"title":1191,"description":1192,"published":1187,"category":659,"image":1193,"draft":663},"\u002Fblog\u002Fsource-maps-exposed-in-production","Source maps expostos: seu app está publicando o código original","Um source map exposto deixa qualquer um ler o código original do seu app, comentários incluídos. A verificação de 30 segundos e o que importa de verdade.","\u002Fblog\u002Fsource-maps-exposed-in-production\u002Fcard-800x500.png",{"path":1195,"title":1196,"description":1197,"published":1198,"category":659,"image":1199,"draft":663},"\u002Fblog\u002Fsupabase-new-api-keys","Supabase publishable key: é a mesma coisa que a anon key?","O Supabase trocou anon e service_role por chaves publicáveis e secretas. Qual delas pertence ao seu app e qual nunca deve estar lá?","2026-08-12","\u002Fblog\u002Fsupabase-new-api-keys\u002Fcard-800x500.png",{"path":1201,"title":1202,"description":1203,"published":1204,"category":759,"image":1205,"draft":663},"\u002Fblog\u002Fdoes-supabase-back-up-my-database","O Supabase faz backup do meu banco de dados? Depende do plano.","O Supabase faz backup do seu banco de dados? Todo dia nos planos pagos, e nada no gratuito. Como descobrir o que você tem e o que essa cópia não cobre.","2026-08-11","\u002Fblog\u002Fdoes-supabase-back-up-my-database\u002Fcard-800x500.png",{"path":1207,"title":1208,"description":1209,"published":1210,"category":659,"image":1211,"draft":663},"\u002Fblog\u002Fsupabase-rls-on-but-table-still-public","O Supabase Row Level Security está ligado. A tabela continua pública.","Ligar o Supabase Row Level Security não protege uma tabela. Quem protege são as políticas, e a que consertou o seu app pode deixar qualquer um entrar.","2026-08-10","\u002Fblog\u002Fsupabase-rls-on-but-table-still-public\u002Fcard-800x500.png",{"path":1213,"title":1214,"description":1215,"published":1210,"category":759,"image":1216,"draft":663},"\u002Fblog\u002Fversion-history-is-not-a-backup","O histórico de versões não é backup. Ele não desfaz um delete.","Lovable e Bolt guardam um histórico de versões do seu código. Seu banco de dados é um serviço à parte, e voltar atrás não traz os seus dados de volta.","\u002Fblog\u002Fversion-history-is-not-a-backup\u002Fcard-800x500.png",{"path":1218,"title":1219,"description":1220,"published":1221,"category":759,"image":1222,"draft":663},"\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database","Três formas de fazer backup de um banco Supabase, e o que falta","O painel, o pg_dump e um serviço gerenciado. O que cada um salva de verdade, o que deixa de fora sem avisar e qual sobrevive a perder a conta.","2026-08-09","\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database\u002Fcard-800x500.png",{"path":1224,"title":1225,"description":1226,"published":1221,"category":659,"image":1227,"draft":663},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","Quais chaves de API são seguras no navegador e quais não são","Sua chave anon do Supabase foi feita para ser pública. Sua chave service_role não, e ela ignora todas as regras que você criar. Como diferenciar as duas.","\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend\u002Fcard-800x500.png",1794213648640]