[{"data":1,"prerenderedAt":1370},["ShallowReactive",2],{"blog-pt-duplicate-key-value-violates-unique-constraint":3,"blog-index-pt":842},{"id":4,"title":5,"body":6,"category":800,"cover":801,"coverAlt":802,"description":803,"draft":804,"extension":805,"faq":806,"image":821,"keywords":822,"meta":831,"navigation":832,"ogTitle":5,"path":833,"published":834,"seo":835,"stem":836,"tldr":837,"updated":834,"__hash__":841},"blog_pt\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint.md","Supabase \"duplicate key value violates unique constraint\", explicado",{"type":7,"value":8,"toc":788},"minimark",[9,13,24,31,34,39,42,45,48,167,180,184,193,267,270,278,293,297,300,303,325,338,344,367,380,386,390,393,400,408,414,420,433,444,448,451,478,515,521,541,557,560,565,569,572,593,608,616,632,635,641,662,667,671,674,683,706,715,733,737,778],[10,11,12],"p",{},"Alguém preenche o formulário da sua lista de espera, espera e aperta o botão de\nnovo. O primeiro clique já tinha passado. O segundo volta como erro, e o console\ndo navegador mostra tudo:",[14,15,20],"pre",{"className":16,"code":18,"language":19},[17],"language-text","{\n  \"code\": \"23505\",\n  \"details\": \"Key (email)=(maria@example.com) already exists.\",\n  \"hint\": null,\n  \"message\": \"duplicate key value violates unique constraint \\\"waitlist_email_key\\\"\"\n}\n","text",[21,22,18],"code",{"__ignoreMap":23},"",[10,25,26,27,30],{},"Supabase \"duplicate key value violates unique constraint\" significa que o Postgres\nse recusou a adicionar a linha porque outra linha já tem o mesmo valor em uma\ncoluna que aceita cada valor uma vez só. Aqui a coluna é ",[21,28,29],{},"email",", e a Maria já\nestá na sua lista.",[10,32,33],{},"O conselho que circula para esse erro é trocar o insert por um upsert. Às vezes\nisso está certo. Numa tabela em que seus visitantes só podem adicionar, porém, um\nupsert pelo navegador precisa de regras que deixem eles lerem a tabela, e a linha\ncom o endereço da Maria é algo que seu app nunca deveria mostrar a um visitante.",[35,36,38],"h2",{"id":37},"o-que-significa-duplicate-key-value-violates-unique-constraint","O que significa \"duplicate key value violates unique constraint\"?",[10,40,41],{},"Significa que o valor que você queria salvar já está em uma coluna que aceita\ncada valor uma vez, então o Postgres recusou a linha inteira. Nada foi gravado, e\na linha que chegou primeiro continua igual.",[10,43,44],{},"Pense na tabela como a lista de convidados de uma porta, uma linha por nome. Uma\nrestrição única é a regra do porteiro: um nome entra na lista uma vez só. Seu app\nchegou com um nome que já estava escrito, e ele barrou.",[10,46,47],{},"Cada parte do erro diz uma coisa:",[49,50,51,67],"table",{},[52,53,54],"thead",{},[55,56,57,61,64],"tr",{},[58,59,60],"th",{},"Campo",[58,62,63],{},"Exemplo",[58,65,66],{},"O que ele diz",[68,69,70,96,111,126,141],"tbody",{},[55,71,72,77,82],{},[73,74,75],"td",{},[21,76,21],{},[73,78,79],{},[21,80,81],{},"23505",[73,83,84,85,88,89],{},"O código do Postgres para esse erro, chamado ",[21,86,87],{},"unique_violation"," na ",[90,91,95],"a",{"href":92,"rel":93},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Futils\u002Ferrcodes.txt",[94],"nofollow","lista de códigos dele",[55,97,98,103,108],{},[73,99,100],{},[21,101,102],{},"message",[73,104,105],{},[21,106,107],{},"duplicate key value violates unique constraint \"waitlist_email_key\"",[73,109,110],{},"Qual regra recusou a linha, pelo nome",[55,112,113,118,123],{},[73,114,115],{},[21,116,117],{},"details",[73,119,120],{},[21,121,122],{},"Key (email)=(maria@example.com) already exists.",[73,124,125],{},"A coluna e o valor que bateram, quando o Postgres decide que quem chamou pode vê-los",[55,127,128,133,138],{},[73,129,130],{},[21,131,132],{},"hint",[73,134,135],{},[21,136,137],{},"null",[73,139,140],{},"Nada a acrescentar para esse erro",[55,142,143,146,151],{},[73,144,145],{},"Status HTTP",[73,147,148],{},[21,149,150],{},"409 Conflict",[73,152,153,154,157,158,160,161,166],{},"Como o PostgREST, a parte do Supabase que transforma as chamadas ",[21,155,156],{},"supabase.from(...)"," em SQL, informa o ",[21,159,81],{}," (",[90,162,165],{"href":163,"rel":164},"https:\u002F\u002Fgithub.com\u002FPostgREST\u002Fpostgrest\u002Fblob\u002Fmain\u002Fsrc\u002Flibrary\u002FPostgREST\u002FError.hs",[94],"o código dele",")",[10,168,169,170,175,176,179],{},"O texto é do próprio Postgres.\n",[90,171,174],{"href":172,"rel":173},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Faccess\u002Fnbtree\u002Fnbtinsert.c",[94],"O código que dispara o erro","\nsó acrescenta a linha ",[21,177,178],{},"Key ... already exists"," quando tem uma descrição da chave\npara dar, e uma seção mais abaixo mostra quando ele não tem.",[35,181,183],{"id":182},"qual-coluna-causou-o-erro","Qual coluna causou o erro?",[10,185,186,187,192],{},"O nome entre aspas diz, porque o Postgres monta o nome a partir da tabela e das\ncolunas, a não ser que alguém tenha escolhido outro.\n",[90,188,191],{"href":189,"rel":190},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Fcommands\u002Findexcmds.c",[94],"O código de nomes dele","\ndá três formatos:",[49,194,195,207],{},[52,196,197],{},[55,198,199,202,205],{},[58,200,201],{},"O nome termina em",[58,203,204],{},"É",[58,206,63],{},[68,208,209,233,251],{},[55,210,211,216,221],{},[73,212,213],{},[21,214,215],{},"_key",[73,217,218,219],{},"uma restrição única nas colunas entre o nome da tabela e ",[21,220,215],{},[73,222,223,226,227,230,231],{},[21,224,225],{},"waitlist_email_key",": tabela ",[21,228,229],{},"waitlist",", coluna ",[21,232,29],{},[55,234,235,240,246],{},[73,236,237],{},[21,238,239],{},"_pkey",[73,241,242,243],{},"a chave primária da tabela, em geral ",[21,244,245],{},"id",[73,247,248],{},[21,249,250],{},"orders_pkey",[55,252,253,259,262],{},[73,254,255,258],{},[21,256,257],{},"_idx",", ou outra coisa",[73,260,261],{},"um índice único criado à mão, ou uma restrição que alguém batizou",[73,263,264],{},[21,265,266],{},"profiles_username_idx",[10,268,269],{},"A mensagem diz \"unique constraint\" nos três casos. Para ver exatamente o que um\nnome cobre, rode isto no SQL Editor com o nome da sua mensagem:",[14,271,276],{"className":272,"code":274,"language":275,"meta":23},[273],"language-sql","select tablename, indexdef\nfrom pg_indexes\nwhere indexname = 'waitlist_email_key';\n","sql",[21,277,274],{"__ignoreMap":23},[10,279,280,281,284,285,288,289,292],{},"O ",[21,282,283],{},"indexdef"," volta como algo assim:\n",[21,286,287],{},"CREATE UNIQUE INDEX waitlist_email_key ON public.waitlist USING btree (email)",",\ne as colunas entre parênteses são as que não podem se repetir. Uma regra em duas\ncolunas, como ",[21,290,291],{},"(team_id, email)",", deixa o mesmo endereço entrar em dois times e o\nrecusa na segunda vez no mesmo time.",[35,294,296],{"id":295},"key-id1-already-exists-quando-você-nunca-enviou-um-id","\"Key (id)=(1) already exists\" quando você nunca enviou um id",[10,298,299],{},"Esse significa que o contador de id da tabela está atrás das linhas que ela já\ntem, então ele entregou um número ocupado. Seu app não enviou id nenhum. O\nPostgres pegou o próximo número do contador, e já existia uma linha com esse\nnúmero.",[10,301,302],{},"Na lista de convidados, o porteiro também distribui pulseiras numeradas de um\nrolo. Alguém copiou à mão os duzentos primeiros convidados, números das pulseiras\nincluídos, e ninguém girou o rolo. Ele ainda começa no 1.",[10,304,305,306,311,312,317,318,320,321,324],{},"Uma coluna que se numera sozinha pega cada número de uma sequência, o nome que o\nPostgres dá a esse contador. A\n",[90,307,310],{"href":308,"rel":309},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fddl-identity-columns.html",[94],"página do Postgres sobre colunas identity","\ndiz que uma coluna assim \"does not guarantee uniqueness\", e cita este caso entre\nos motivos: valores que \"could be inserted manually into the identity column\".\nLinhas chegam com o número já preenchido de uma importação de CSV que inclui a\ncoluna id, de uma cópia de outro projeto ou de um script de seed. O contador não\nfica sabendo de nenhuma.\n",[90,313,316],{"href":314,"rel":315},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Ftroubleshooting\u002Fwhy-are-there-gaps-in-my-postgres-id-sequence-Frifus",[94],"A página de solução de problemas do Supabase sobre sequências de id","\ntrata exatamente essa mensagem em um ",[21,319,239],{},", e a correção dela também é um\n",[21,322,323],{},"setval",".",[10,326,327,328,331,332,334,335,337],{},"A correção é uma instrução só. Troque ",[21,329,330],{},"orders"," e ",[21,333,245],{}," pela sua tabela e pela sua\ncoluna; a tabela é a parte do nome antes de ",[21,336,239],{},":",[14,339,342],{"className":340,"code":341,"language":275,"meta":23},[273],"select setval(\n  pg_get_serial_sequence('public.orders', 'id'),\n  (select max(id) from public.orders)\n);\n",[21,343,341],{"__ignoreMap":23},[10,345,280,346,349,350,355,356,362,363,324],{},[21,347,348],{},"pg_get_serial_sequence"," acha o contador por trás da coluna, e\n",[90,351,354],{"href":352,"rel":353},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Ffunctions-info.html",[94],"a documentação do Postgres","\ndiz que ele funciona para os dois tipos: \"If the column is an identity column, the\nassociated sequence is the sequence internally created for that column.\" Depois,\no ",[90,357,360],{"href":358,"rel":359},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Ffunctions-sequence.html",[94],[21,361,323],{}," com\ndois argumentos ajusta o contador para que \"the next nextval will advance the\nsequence before returning a value\". A próxima linha nova recebe o maior id mais\num. Se o Postgres responder que a relação não existe, a tabela se escreve de outro\njeito, tem uma letra maiúscula ou está em outro schema, e\n",[90,364,366],{"href":365},"\u002Fblog\u002Fsupabase-relation-does-not-exist","esse erro tem um post só dele",[10,368,369,370,373,374,379],{},"Uma restauração a partir da saída do ",[21,371,372],{},"pg_dump"," não traz você até aqui.\n",[90,375,378],{"href":376,"rel":377},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fapp-pgdump.html",[94],"A documentação dele","\nlista o que um dump de dados leva: \"Table data, large objects, and sequence values\nare dumped.\" Cada contador volta com as linhas dele.",[381,382],"diagram",{"alt":383,"caption":384,"src":385},"Uma lista de cinco linhas numeradas, de 1 a 5, ao lado de um contador. À esquerda o contador marca 1 e aponta para a linha 1, acesa em âmbar, que termina numa placa com Key (id)=(1). À direita o contador marca 6 e aponta para um nome novo na sexta linha, com um sinal de certo embaixo.","Linhas copiadas com os próprios ids deixam o contador onde estava. O setval leva o contador além do maior id, e a linha seguinte recebe um número livre.","\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint\u002Fthe-counter-is-behind-1600x640.png",[35,387,389],{"id":388},"as-três-formas-de-lidar-com-um-duplicado","As três formas de lidar com um duplicado",[10,391,392],{},"Decida o que deve acontecer quando o valor já está lá: recusar a linha nova e\ndizer isso, deixar que ela substitua a antiga ou parar de enviá-la.",[10,394,395,399],{},[396,397,398],"strong",{},"Recuse, com as suas palavras."," Serve quando uma segunda linha seria um erro: um\nendereço já cadastrado, um nome de usuário já em uso. O insert falha, seu app lê o\ncódigo e a pessoa vê uma frase escrita por você:",[14,401,406],{"className":402,"code":404,"language":405,"meta":23},[403],"language-js","const { error } = await supabase.from('profiles').insert({ id: user.id, username })\n\nif (error?.code === '23505') {\n  setMessage('Esse nome de usuário já está em uso. Tente outro.')\n} else if (error) {\n  setMessage('Algo deu errado. Tente de novo.')\n}\n","js",[21,407,404],{"__ignoreMap":23},[10,409,410,413],{},[396,411,412],{},"Deixe substituir a antiga."," Serve quando a versão mais nova deve ganhar: uma\nlinha de configurações por usuário, um carrinho por sessão. Isso é um upsert, e a\npróxima seção trata de quando ele dá errado.",[10,415,416,419],{},[396,417,418],{},"Pare de enviar."," Às vezes a segunda linha nunca deveria ter existido, e ela vem\nde um destes lugares:",[421,422,423,427,430],"ul",{},[424,425,426],"li",{},"um botão que envia o formulário de novo quando é apertado de novo. Desative o\nbotão até a primeira requisição responder.",[424,428,429],{},"uma nova tentativa depois de uma resposta lenta, quando a primeira tinha passado",[424,431,432],{},"dois escritores para a mesma linha, como um trigger que cria o perfil de cada\nusuário novo e código do app que insere o mesmo perfil depois do cadastro",[10,434,435,436,440,441,443],{},"Vale reconhecer o caso do trigger de cara. Um trigger que copia usuários novos\npara uma tabela sua falha com esse erro quando o valor copiado já está lá, e o\nSupabase informa o cadastro inteiro como\n",[90,437,439],{"href":438},"\u002Fblog\u002Fdatabase-error-saving-new-user","\"Database error saving new user\"",". O\n",[21,442,81],{}," em si fica nos logs do Postgres, e esse post mostra onde.",[35,445,447],{"id":446},"você-deve-usar-upsert","Você deve usar upsert?",[10,449,450],{},"Use quando a linha nova deve substituir a antiga, e confira três coisas antes de\ncontar com ele.",[10,452,280,453,456,457,462,463,466,467,470,471,474,475,324],{},[21,454,455],{},".upsert()"," do supabase-js é um insert que faz outra coisa quando a linha\nexiste. ",[90,458,461],{"href":459,"rel":460},"https:\u002F\u002Fgithub.com\u002Fsupabase\u002Fsupabase-js\u002Fblob\u002Fmaster\u002Fpackages\u002Fcore\u002Fpostgrest-js\u002Fsrc\u002FPostgrestQueryBuilder.ts",[94],"O código-fonte dele","\ndiz isso em duas linhas: \"If ",[21,464,465],{},"true",", duplicate rows are ignored. If ",[21,468,469],{},"false",",\nduplicate rows are merged with existing rows.\" Essa é a opção ",[21,472,473],{},"ignoreDuplicates",",\ne o Postgres roda as duas versões como um ",[21,476,477],{},"insert ... on conflict",[10,479,480,487,492,493,496,497,499,500,502,503,505,506,508,509,514],{},[396,481,482,483,486],{},"1. O ",[21,484,485],{},"onConflict"," tem que nomear a coluna do seu erro.",[90,488,491],{"href":489,"rel":490},"https:\u002F\u002Fdocs.postgrest.org\u002Fen\u002Fstable\u002Freferences\u002Fapi\u002Ftables_views.html",[94],"A documentação do PostgREST","\ndiz: \"By default, upsert operates based on the primary key columns\". Um upsert de\n",[21,494,495],{},"{ email }"," numa tabela cuja chave primária é ",[21,498,245],{}," procura um conflito em ",[21,501,245],{},", não\nacha porque o id é novo, insere e esbarra na regra de ",[21,504,29],{},", com o mesmo\n",[21,507,81],{},". A\n",[90,510,513],{"href":511,"rel":512},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fsql-insert.html",[94],"referência de INSERT do Postgres","\ndiz que a ação alternativa acontece \"if an arbiter constraint or index specified\nby conflict_target is violated\". Um conflito em qualquer outra coluna recusa a\nlinha como antes.",[14,516,519],{"className":517,"code":518,"language":405,"meta":23},[403],"await supabase\n  .from('settings')\n  .upsert({ user_id: user.id, theme: 'dark' }, { onConflict: 'user_id' })\n",[21,520,518],{"__ignoreMap":23},[10,522,523,526,527,530,531,534,535,540],{},[396,524,525],{},"2. Essa coluna precisa de uma regra única própria."," Sem ela, o Postgres não tem\ncom o que comparar e responde\n",[21,528,529],{},"there is no unique or exclusion constraint matching the ON CONFLICT specification",",\ncódigo ",[21,532,533],{},"42P10","\n(",[90,536,539],{"href":537,"rel":538},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Foptimizer\u002Futil\u002Fplancat.c",[94],"a linha que dispara isso",").",[10,542,543,546,547,552,553,324],{},[396,544,545],{},"3. Pelo navegador, a Row Level Security lê mais regras suas."," A Row Level\nSecurity é o conjunto de regras por linha que o Supabase confere em cada\nrequisição feita com a sua chave pública. Para um upsert, a\n",[90,548,551],{"href":549,"rel":550},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fsql-createpolicy.html",[94],"referência do Postgres sobre policies","\ndiz: \"SELECT permissions are required on the relation, and the rows proposed for\ninsertion are checked using the relation's SELECT policies.\" Quando a linha existe\ne o upsert mescla, a linha existente também é conferida contra as regras de\natualização da tabela. Uma linha que falha em qualquer uma dessas conferências\nencerra a requisição com\n",[90,554,556],{"href":555},"\u002Fblog\u002Fnew-row-violates-row-level-security-policy","new row violates row-level security policy",[10,558,559],{},"Então, na lista de espera, um upsert pelo navegador falha enquanto a tabela não\ntiver uma regra de leitura que um visitante cumpra. Uma regra de leitura que um\nestranho cumpre para qualquer endereço é uma regra que deixa estranhos lerem a sua\nlista. Na porta, reescrever uma linha quer dizer que antes mostraram a lista para\nvocê. Numa tabela assim, prefira recusar o duplicado, ou use a função da próxima\nseção.",[381,561],{"alt":562,"caption":563,"src":564},"Duas requisições para a mesma tabela. Um insert passa por uma cancela, insert, e chega até ela. Um upsert tem que passar por três cancelas seguidas, insert, select e update; a cancela select está fechada e desenhada em âmbar, então o upsert para ali.","Pelo navegador, um upsert é conferido contra as regras de leitura e de atualização da tabela além da regra de inserção. Numa tabela em que visitantes só podem adicionar, é a regra de leitura que o para.","\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint\u002Fupsert-reads-three-rules-1600x660.png",[35,566,568],{"id":567},"é-seguro-mostrar-esse-erro-aos-seus-usuários","É seguro mostrar esse erro aos seus usuários?",[10,570,571],{},"Não. Mostre uma frase sua e guarde o erro cru nos seus logs, por dois motivos: a\nlinha de detalhe pode devolver o valor, e o erro confirma que o valor existe, seja\nlá o que você mostre.",[10,573,574,577,578,581,582,589,590,324],{},[396,575,576],{},"A linha de detalhe repete o valor."," ",[21,579,580],{},"Key (email)=(maria@example.com)"," é\ninofensiva na tela da Maria e é outra história num print postado num fórum de\nsuporte, numa ferramenta de rastreamento de erros ou na tela de alguém que digitou\no endereço de outra pessoa no seu formulário. O Postgres já protege essa linha. A\nfunção que monta a linha\n(",[90,583,586],{"href":584,"rel":585},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Faccess\u002Findex\u002Fgenam.c",[94],[21,587,588],{},"BuildIndexValueDescription",")\nconfere primeiro: \"if RLS is enabled for the relation. If so, return NULL to avoid\nleaking data.\" Uma requisição feita com a sua chave pública para uma tabela com\nRow Level Security ligada recebe ",[21,591,592],{},"\"details\": null",[10,594,595,596,599,600,602,603,607],{},"Lida ao contrário, a linha diz algo sobre a tabela. Se o console do seu navegador\nmostra ",[21,597,598],{},"Key (...)=(...)"," para uma requisição que seu app fez com a chave pública, a\nRow Level Security está desligada nessa tabela. A única exceção é uma requisição\nque seu app envia por código de servidor com a chave secreta, que pula todas as\nregras. O ",[21,601,137],{}," prova menos: o Postgres também tira a linha quando quem chamou não\npode ler todas as colunas da chave.\n",[90,604,606],{"href":605},"\u002Fblog\u002Fenable-rls-on-every-supabase-table","Ligar a Row Level Security em toda tabela","\nmostra a chave e a primeira regra.",[10,609,610,611,615],{},"Uma tabela com as regras desligadas responde a um estranho com a mesma facilidade\ncom que respondeu a você. Nosso ",[90,612,614],{"href":613},"\u002Fsecurity-scanner","scan gratuito"," lê seu app no ar\ncom a mesma chave pública que um visitante recebe, informa quais tabelas devolvem\nlinhas e não pede conta.",[10,617,618,621,622,625,626,631],{},[396,619,620],{},"O erro responde a uma pergunta que talvez você não queira responder."," Mesmo sem\na linha de detalhe, um ",[21,623,624],{},"409"," conta a quem enviou a linha que o valor já está lá. A\n",[90,627,630],{"href":628,"rel":629},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fddl-rowsecurity.html",[94],"página do Postgres sobre segurança de linhas","\ndiz que restrições únicas \"always bypass row security to ensure that data\nintegrity is maintained\", e pede que você desenhe tabelas e regras para que essas\nverificações não virem um canal escondido. Na lista de espera, qualquer pessoa\ndescobre se um endereço está na sua lista tentando adicioná-lo. O porteiro nunca\nlê o nome em voz alta, e quando ele barra alguém, a fila fica sabendo mesmo assim\nque o nome estava lá.",[10,633,634],{},"Se isso importa para a sua lista, dê ao navegador a mesma resposta nos dois casos.\nUma função no banco pode adicionar o endereço, pular se ele já estiver lá e não\ndevolver nada em nenhum dos dois casos:",[14,636,639],{"className":637,"code":638,"language":275,"meta":23},[273],"create function public.join_waitlist(new_email text)\nreturns void\nlanguage sql\nsecurity definer\nset search_path = ''\nas $$\n  insert into public.waitlist (email)\n  values (new_email)\n  on conflict (email) do nothing;\n$$;\n",[21,640,638],{"__ignoreMap":23},[10,642,643,644,440,647,650,651,656,657,661],{},"Seu app chama ",[21,645,646],{},"supabase.rpc('join_waitlist', { new_email: email })",[21,648,649],{},"security definer"," executa o insert como o dono da função, então os visitantes não\nprecisam de regra nenhuma na tabela, e a regra que deixava eles inserirem pode\nsair, senão o caminho antigo continua aberto ao lado do novo.\n",[90,652,653,654],{"href":438},"O que \"Database error saving new user\" diz sobre ",[21,655,649],{},"\nexplica o que ele entrega, e\n",[90,658,660],{"href":659},"\u002Fblog\u002Fsupabase-function-not-found","chamar uma função do Supabase"," mostra como\ncasar os nomes dos argumentos.",[381,663],{"alt":664,"caption":665,"src":666},"O mesmo duplicado enviado a duas tabelas. A tabela da esquerda tem um cadeado, e a resposta dela diz 23505 e 409 com uma linha details vazia. A tabela da direita não tem, e a resposta dela traz uma linha details em âmbar com Key (email)=(…).","O Postgres só escreve o valor na linha de detalhe para quem chama sem que a Row Level Security se aplique. Ver o valor pelo navegador quer dizer que as regras da tabela estão desligadas.","\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint\u002Fthe-detail-line-and-rls-1600x700.png",[35,668,670],{"id":669},"antes-de-mudar-como-seu-app-grava","Antes de mudar como seu app grava",[10,672,673],{},"Guarde uma cópia do banco de antes de trocar um insert por upsert ou de acertar um\ncontador, e fique sabendo quando uma tabela começar a responder a estranhos.",[10,675,676],{},[396,677,678,682],{},[90,679,681],{"href":680},"\u002Fsupabase-backups","Reeve Care"," guarda uma cópia do seu banco no Supabase:",[421,684,685,688,691,694,697,700,703],{},[424,686,687],{},"uma cópia criptografada num cronograma, no armazenamento próprio da Reeve na UE, fora da sua conta do Supabase",[424,689,690],{},"cada tabela com as linhas como estavam antes de uma mesclagem gravar por cima",[424,692,693],{},"o contador de id de cada tabela volta com as linhas dela numa restauração, para que a próxima linha nova receba um número livre",[424,695,696],{},"cada cópia verificada antes de valer, contando as linhas de cada tabela",[424,698,699],{},"seus arquivos enviados também, assim que você conecta uma credencial do Storage",[424,701,702],{},"uma restauração com um clique, com uma cópia de segurança do estado atual feita antes",[424,704,705],{},"tudo o que o Monitor faz",[10,707,708],{},[396,709,710,714],{},[90,711,713],{"href":712},"\u002Fwebsite-monitoring","Reeve Monitor"," continua conferindo de fora:",[421,716,717,720,723,730],{},[424,718,719],{},"se seu app está no ar, conferido a cada minuto",[424,721,722],{},"a verificação de segurança completa a cada hora, em até três apps",[424,724,725,726,729],{},"uma mensagem quando um resultado ",[396,727,728],{},"muda",", para que uma tabela que começa a responder a estranhos depois de um deploy chegue até você sem você ter que ir olhar",[424,731,732],{},"um relatório mensal, em linguagem simples, do que ele viu",[35,734,736],{"id":735},"o-que-fazer-agora","O que fazer agora",[738,739,740],"key-takeaways",{},[421,741,742,751,760,766,772],{},[424,743,744,745,747,748,750],{},"Leia o nome entre aspas. A tabela e a coluna vêm antes de ",[21,746,215],{},", e ",[21,749,239],{}," quer dizer chave primária.",[424,752,753,754,756,757,759],{},"Se o nome termina em ",[21,755,239],{}," e seu app não envia id, acerte o contador com a instrução ",[21,758,323],{}," acima.",[424,761,762,763,765],{},"Capture o ",[21,764,81],{}," no seu app e mostre uma frase escrita por você, nunca a mensagem crua.",[424,767,768,769,771],{},"Use upsert só onde a linha nova deve substituir a antiga, com o ",[21,770,485],{}," na coluna do erro.",[424,773,774,775,777],{},"Se o console do navegador mostra uma linha ",[21,776,598],{}," para uma requisição feita com a sua chave pública, ligue a Row Level Security nessa tabela.",[10,779,780,781,783,784,787],{},"Comece pelo nome entre aspas, porque ele diz qual regra recusou a linha e qual\ncoluna essa regra protege. Antes de rodar um ",[21,782,323],{},", um upsert ou qualquer outra\ncoisa que reescreva linhas das quais as pessoas dependem,\n",[90,785,786],{"href":680},"guarde uma cópia do banco"," de antes da mudança.",{"title":23,"searchDepth":789,"depth":789,"links":790},3,[791,793,794,795,796,797,798,799],{"id":37,"depth":792,"text":38},2,{"id":182,"depth":792,"text":183},{"id":295,"depth":792,"text":296},{"id":388,"depth":792,"text":389},{"id":446,"depth":792,"text":447},{"id":567,"depth":792,"text":568},{"id":669,"depth":792,"text":670},{"id":735,"depth":792,"text":736},"Noções de segurança","\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint\u002Fcover-1200x630.png","Uma lista de nomes numa prancheta, com uma linha em turquesa, e na frente um papel com o mesmo nome em âmbar, mantido fora da lista.","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.",false,"md",[807,810,812,815,818],{"q":808,"a":809},"O que significa duplicate key value violates unique constraint?","Significa que o Postgres se recusou a adicionar ou alterar uma linha porque outra linha já tem o mesmo valor em uma coluna, ou grupo de colunas, que aceita cada valor uma vez só. Nada foi gravado. O código é 23505, e de um app do Supabase ele chega como HTTP 409.",{"q":183,"a":811},"O nome da regra entre aspas diz. O Postgres nomeia uma restrição única pela tabela e pelas colunas, com _key no fim, então waitlist_email_key é a coluna email da tabela waitlist, e um nome terminado em _pkey é a chave primária, em geral id. A linha de detalhe, quando aparece, nomeia a coluna e o valor.",{"q":813,"a":814},"Devo usar upsert?","Só quando a linha nova deve substituir a antiga, como uma linha de configurações por usuário. Coloque o onConflict na coluna do erro, porque os upserts do Supabase verificam a chave primária por padrão. Pelo navegador, um upsert também precisa de regras de leitura que a linha cumpra, então numa tabela em que os visitantes só podem adicionar ele falha, ou obriga você a abrir a tabela para leitura.",{"q":816,"a":817},"Por que isso acontece no cadastro?","Há dois caminhos: um trigger copia cada usuário novo para uma tabela sua e o valor copiado já está lá, ou seu app insere a mesma linha de perfil que o trigger já criou. O Supabase informa os dois como \"Database error saving new user\", e o 23505 em si fica nos logs do Postgres.",{"q":819,"a":820},"É seguro mostrar esse erro aos usuários?","Não. A linha de detalhe pode repetir o valor, a mensagem nomeia sua tabela e sua coluna, e o próprio erro conta a quem enviou a linha que o valor existe. Capture o código 23505, mostre uma frase escrita por você e guarde o erro cru nos seus próprios logs.","\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint\u002Fcard-800x500.png",[823,824,825,826,827,828,829,830],"duplicate key value violates unique constraint","supabase erro chave duplicada","postgres 23505","supabase upsert duplicado","key already exists","duplicate key value violates unique constraint pkey","there is no unique or exclusion constraint matching the on conflict specification","supabase onconflict",{},true,"\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint","2026-10-24",{"title":5,"description":803},"blog\u002Fduplicate-key-value-violates-unique-constraint",[838,839,840],"Supabase \"duplicate key value violates unique constraint\" (código do Postgres 23505, HTTP 409 no seu app) significa que uma linha já tem esse valor em uma coluna que aceita cada valor uma vez só. O nome entre aspas diz qual coluna.","\"Key (id)=(1) already exists\" quando seu app não envia nenhum id significa que o contador de id da tabela está atrás das linhas dela. Uma linha com setval acerta isso.","Upsert só ajuda se o onConflict nomear essa coluna, e pelo navegador ele também precisa das regras de leitura da tabela. Mostre aos seus usuários uma frase sua: a mensagem crua pode devolver o valor.","C0Xnl5QN2NKtU2or3K1PzpUfoMWOXV5JEocpWmJtdAk",[843,849,855,861,867,873,879,885,891,897,903,910,916,922,928,934,940,941,946,951,957,963,969,975,981,987,993,998,1004,1010,1016,1022,1028,1034,1040,1046,1052,1058,1064,1070,1076,1082,1088,1094,1100,1106,1111,1117,1123,1129,1135,1141,1147,1153,1159,1165,1171,1177,1183,1189,1195,1201,1206,1212,1218,1224,1230,1236,1242,1248,1254,1260,1266,1272,1278,1284,1289,1295,1301,1307,1313,1319,1325,1331,1336,1342,1348,1354,1359,1365],{"path":844,"title":845,"description":846,"published":847,"category":800,"image":848,"draft":804},"\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":850,"title":851,"description":852,"published":853,"category":800,"image":854,"draft":804},"\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":856,"title":857,"description":858,"published":859,"category":800,"image":860,"draft":804},"\u002Fblog\u002Fviolates-foreign-key-constraint","Erro do Supabase \"violates foreign key constraint\" ao inserir","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.","2026-11-07","\u002Fblog\u002Fviolates-foreign-key-constraint\u002Fcard-800x500.png",{"path":862,"title":863,"description":864,"published":865,"category":800,"image":866,"draft":804},"\u002Fblog\u002Fnull-value-violates-not-null-constraint","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":868,"title":869,"description":870,"published":871,"category":800,"image":872,"draft":804},"\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":874,"title":875,"description":876,"published":877,"category":800,"image":878,"draft":804},"\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":880,"title":881,"description":882,"published":883,"category":800,"image":884,"draft":804},"\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":886,"title":887,"description":888,"published":889,"category":800,"image":890,"draft":804},"\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":892,"title":893,"description":894,"published":895,"category":800,"image":896,"draft":804},"\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":898,"title":899,"description":900,"published":901,"category":800,"image":902,"draft":804},"\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":904,"title":905,"description":906,"published":907,"category":908,"image":909,"draft":804},"\u002Fblog\u002Fdatabase-error-deleting-user","\"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":911,"title":912,"description":913,"published":914,"category":800,"image":915,"draft":804},"\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":917,"title":918,"description":919,"published":920,"category":800,"image":921,"draft":804},"\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":923,"title":924,"description":925,"published":926,"category":800,"image":927,"draft":804},"\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":929,"title":930,"description":931,"published":932,"category":800,"image":933,"draft":804},"\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":935,"title":936,"description":937,"published":938,"category":800,"image":939,"draft":804},"\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":833,"title":5,"description":803,"published":834,"category":800,"image":821,"draft":804},{"path":365,"title":942,"description":943,"published":944,"category":800,"image":945,"draft":804},"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":659,"title":947,"description":948,"published":949,"category":800,"image":950,"draft":804},"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":952,"title":953,"description":954,"published":955,"category":800,"image":956,"draft":804},"\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":958,"title":959,"description":960,"published":961,"category":800,"image":962,"draft":804},"\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":964,"title":965,"description":966,"published":967,"category":800,"image":968,"draft":804},"\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":970,"title":971,"description":972,"published":973,"category":800,"image":974,"draft":804},"\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":976,"title":977,"description":978,"published":979,"category":800,"image":980,"draft":804},"\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":982,"title":983,"description":984,"published":985,"category":800,"image":986,"draft":804},"\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":988,"title":989,"description":990,"published":991,"category":800,"image":992,"draft":804},"\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":438,"title":994,"description":995,"published":996,"category":800,"image":997,"draft":804},"\"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":999,"title":1000,"description":1001,"published":1002,"category":800,"image":1003,"draft":804},"\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":1005,"title":1006,"description":1007,"published":1008,"category":800,"image":1009,"draft":804},"\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":1011,"title":1012,"description":1013,"published":1014,"category":800,"image":1015,"draft":804},"\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":1017,"title":1018,"description":1019,"published":1020,"category":800,"image":1021,"draft":804},"\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":1023,"title":1024,"description":1025,"published":1026,"category":800,"image":1027,"draft":804},"\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":1029,"title":1030,"description":1031,"published":1032,"category":800,"image":1033,"draft":804},"\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":1035,"title":1036,"description":1037,"published":1038,"category":800,"image":1039,"draft":804},"\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":1041,"title":1042,"description":1043,"published":1044,"category":800,"image":1045,"draft":804},"\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":1047,"title":1048,"description":1049,"published":1050,"category":800,"image":1051,"draft":804},"\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":1053,"title":1054,"description":1055,"published":1056,"category":800,"image":1057,"draft":804},"\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":1059,"title":1060,"description":1061,"published":1062,"category":800,"image":1063,"draft":804},"\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":1065,"title":1066,"description":1067,"published":1068,"category":800,"image":1069,"draft":804},"\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":1071,"title":1072,"description":1073,"published":1074,"category":800,"image":1075,"draft":804},"\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":1077,"title":1078,"description":1079,"published":1080,"category":800,"image":1081,"draft":804},"\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":1083,"title":1084,"description":1085,"published":1086,"category":800,"image":1087,"draft":804},"\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":1089,"title":1090,"description":1091,"published":1092,"category":908,"image":1093,"draft":804},"\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":1095,"title":1096,"description":1097,"published":1098,"category":908,"image":1099,"draft":804},"\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":1101,"title":1102,"description":1103,"published":1104,"category":908,"image":1105,"draft":804},"\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":1107,"title":1108,"description":1109,"published":1104,"category":800,"image":1110,"draft":804},"\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":1112,"title":1113,"description":1114,"published":1115,"category":800,"image":1116,"draft":804},"\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":1118,"title":1119,"description":1120,"published":1121,"category":800,"image":1122,"draft":804},"\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":1124,"title":1125,"description":1126,"published":1127,"category":908,"image":1128,"draft":804},"\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":1130,"title":1131,"description":1132,"published":1133,"category":800,"image":1134,"draft":804},"\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":1136,"title":1137,"description":1138,"published":1139,"category":800,"image":1140,"draft":804},"\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":1142,"title":1143,"description":1144,"published":1145,"category":800,"image":1146,"draft":804},"\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":1148,"title":1149,"description":1150,"published":1151,"category":800,"image":1152,"draft":804},"\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":1154,"title":1155,"description":1156,"published":1157,"category":800,"image":1158,"draft":804},"\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":1160,"title":1161,"description":1162,"published":1163,"category":800,"image":1164,"draft":804},"\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":1166,"title":1167,"description":1168,"published":1169,"category":800,"image":1170,"draft":804},"\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":1172,"title":1173,"description":1174,"published":1175,"category":800,"image":1176,"draft":804},"\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":1178,"title":1179,"description":1180,"published":1181,"category":908,"image":1182,"draft":804},"\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":1184,"title":1185,"description":1186,"published":1187,"category":908,"image":1188,"draft":804},"\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":1190,"title":1191,"description":1192,"published":1193,"category":908,"image":1194,"draft":804},"\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":1196,"title":1197,"description":1198,"published":1199,"category":800,"image":1200,"draft":804},"\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":605,"title":1202,"description":1203,"published":1204,"category":800,"image":1205,"draft":804},"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":1207,"title":1208,"description":1209,"published":1210,"category":800,"image":1211,"draft":804},"\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":1213,"title":1214,"description":1215,"published":1216,"category":800,"image":1217,"draft":804},"\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":1219,"title":1220,"description":1221,"published":1222,"category":800,"image":1223,"draft":804},"\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":1225,"title":1226,"description":1227,"published":1228,"category":800,"image":1229,"draft":804},"\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":1231,"title":1232,"description":1233,"published":1234,"category":800,"image":1235,"draft":804},"\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":1237,"title":1238,"description":1239,"published":1240,"category":800,"image":1241,"draft":804},"\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":1243,"title":1244,"description":1245,"published":1246,"category":800,"image":1247,"draft":804},"\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":1249,"title":1250,"description":1251,"published":1252,"category":800,"image":1253,"draft":804},"\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":1255,"title":1256,"description":1257,"published":1258,"category":908,"image":1259,"draft":804},"\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":1261,"title":1262,"description":1263,"published":1264,"category":800,"image":1265,"draft":804},"\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":1267,"title":1268,"description":1269,"published":1270,"category":908,"image":1271,"draft":804},"\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":1273,"title":1274,"description":1275,"published":1276,"category":800,"image":1277,"draft":804},"\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":1279,"title":1280,"description":1281,"published":1282,"category":800,"image":1283,"draft":804},"\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":555,"title":1285,"description":1286,"published":1287,"category":800,"image":1288,"draft":804},"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":1290,"title":1291,"description":1292,"published":1293,"category":800,"image":1294,"draft":804},"\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":1296,"title":1297,"description":1298,"published":1299,"category":908,"image":1300,"draft":804},"\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":1302,"title":1303,"description":1304,"published":1305,"category":908,"image":1306,"draft":804},"\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":1308,"title":1309,"description":1310,"published":1311,"category":800,"image":1312,"draft":804},"\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":1314,"title":1315,"description":1316,"published":1317,"category":800,"image":1318,"draft":804},"\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":1320,"title":1321,"description":1322,"published":1323,"category":908,"image":1324,"draft":804},"\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":1326,"title":1327,"description":1328,"published":1329,"category":800,"image":1330,"draft":804},"\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":1332,"title":1333,"description":1334,"published":1329,"category":800,"image":1335,"draft":804},"\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":1337,"title":1338,"description":1339,"published":1340,"category":800,"image":1341,"draft":804},"\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":1343,"title":1344,"description":1345,"published":1346,"category":908,"image":1347,"draft":804},"\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":1349,"title":1350,"description":1351,"published":1352,"category":800,"image":1353,"draft":804},"\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":1355,"title":1356,"description":1357,"published":1352,"category":908,"image":1358,"draft":804},"\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":1360,"title":1361,"description":1362,"published":1363,"category":908,"image":1364,"draft":804},"\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":1366,"title":1367,"description":1368,"published":1363,"category":800,"image":1369,"draft":804},"\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",1794213649122]