[{"data":1,"prerenderedAt":1336},["ShallowReactive",2],{"blog-pt-null-value-violates-not-null-constraint":3,"blog-index-pt":810},{"id":4,"title":5,"body":6,"category":768,"cover":769,"coverAlt":770,"description":771,"draft":772,"extension":773,"faq":774,"image":790,"keywords":791,"meta":799,"navigation":800,"ogTitle":5,"path":801,"published":802,"seo":803,"stem":804,"tldr":805,"updated":802,"__hash__":809},"blog_pt\u002Fblog\u002Fnull-value-violates-not-null-constraint.md","Erro do Supabase \"null value in column violates not-null constraint\"",{"type":7,"value":8,"toc":756},"minimark",[9,13,24,35,38,43,46,49,171,180,184,187,201,216,241,258,293,308,312,321,350,367,375,381,387,394,400,404,407,420,426,429,435,443,448,452,455,463,474,484,490,496,514,545,549,552,563,570,573,597,615,618,624,627,633,640,645,649,652,661,683,692,706,710,749],[10,11,12],"p",{},"Você aperta Salvar numa tarefa nova do seu app e nada é salvo. A página não\nmostra erro nenhum e a lista continua igual. O console do navegador tem o\nmotivo:",[14,15,20],"pre",{"className":16,"code":18,"language":19},[17],"language-text","{\n  \"code\": \"23502\",\n  \"details\": null,\n  \"hint\": null,\n  \"message\": \"null value in column \\\"category_id\\\" of relation \\\"tasks\\\" violates not-null constraint\"\n}\n","text",[21,22,18],"code",{"__ignoreMap":23},"",[10,25,26,27,30,31,34],{},"Supabase \"null value in column violates not-null constraint\" significa que uma\nlinha chegou ao seu banco sem nada numa coluna que sempre precisa ter um valor, e\no Postgres recusou a linha inteira. Aqui a coluna é ",[21,28,29],{},"category_id"," na sua tabela\n",[21,32,33],{},"tasks",". O salvamento não mandou nenhuma categoria.",[10,36,37],{},"A correção mais rápida é remover a regra: uma linha de SQL e a coluna pode ficar\nvazia. O erro some, e daí em diante chegam linhas com esse campo em branco, que o\nresto do seu app encontra onde você não estava olhando. Descubra primeiro de onde\nveio o valor vazio. Quase sempre é um de poucos lugares.",[39,40,42],"h2",{"id":41},"o-que-significa-null-value-in-column-violates-not-null-constraint","O que significa \"null value in column violates not-null constraint\"?",[10,44,45],{},"Significa que uma coluna da linha que seu app quis salvar estava vazia, que essa\ncoluna nunca pode ficar vazia, e que o Postgres se recusou a salvar qualquer parte\nda linha. Nada foi gravado. Null é a palavra do Postgres para nenhum valor, o que\nnão é o mesmo que um zero nem que um texto sem letras.",[10,47,48],{},"Pense em cada linha como um formulário de papel e na tabela como a gaveta do\narquivo onde ele é guardado. Uma restrição not-null é um asterisco impresso ao\nlado de um campo. O Postgres fica na gaveta e não arquiva nenhum formulário com um\ncampo de asterisco em branco. O formulário volta, e a mensagem diz qual campo.",[50,51,52,68],"table",{},[53,54,55],"thead",{},[56,57,58,62,65],"tr",{},[59,60,61],"th",{},"Campo",[59,63,64],{},"Exemplo",[59,66,67],{},"O que ele diz",[69,70,71,97,112,131,145],"tbody",{},[56,72,73,78,83],{},[74,75,76],"td",{},[21,77,21],{},[74,79,80],{},[21,81,82],{},"23502",[74,84,85,86,89,90],{},"O código do Postgres para esse erro, chamado ",[21,87,88],{},"not_null_violation"," na ",[91,92,96],"a",{"href":93,"rel":94},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Futils\u002Ferrcodes.txt",[95],"nofollow","lista de códigos dele",[56,98,99,104,109],{},[74,100,101],{},[21,102,103],{},"message",[74,105,106],{},[21,107,108],{},"null value in column \"category_id\" of relation \"tasks\" violates not-null constraint",[74,110,111],{},"A coluna vazia e depois a tabela. \"Relation\" é a palavra do Postgres para tabela",[56,113,114,119,128],{},[74,115,116],{},[21,117,118],{},"details",[74,120,121,124,125],{},[21,122,123],{},"Failing row contains (…)",", ou ",[21,126,127],{},"null",[74,129,130],{},"Uma cópia da linha recusada, quando o Postgres decide que quem chamou pode vê-la. Uma seção mais abaixo explica quando",[56,132,133,138,142],{},[74,134,135],{},[21,136,137],{},"hint",[74,139,140],{},[21,141,127],{},[74,143,144],{},"Nada a acrescentar para esse erro",[56,146,147,150,155],{},[74,148,149],{},"Status HTTP",[74,151,152],{},[21,153,154],{},"400 Bad Request",[74,156,157,158,161,162,164,165,170],{},"O PostgREST, a parte do Supabase que transforma chamadas ",[21,159,160],{},"supabase.from(...)"," em SQL, não tem um caso próprio para ",[21,163,82],{},", então ",[91,166,169],{"href":167,"rel":168},"https:\u002F\u002Fgithub.com\u002FPostgREST\u002Fpostgrest\u002Fblob\u002Fmain\u002Fsrc\u002Flibrary\u002FPostgREST\u002FError.hs",[95],"o código dele"," cai em 400",[10,172,173,174,179],{},"O texto é do próprio Postgres, da\n",[91,175,178],{"href":176,"rel":177},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Fexecutor\u002FexecMain.c",[95],"função que dispara o erro",",\ne se lê igual venha a linha do seu app, de um trigger ou do SQL Editor.",[39,181,183],{"id":182},"de-onde-veio-o-valor-vazio","De onde veio o valor vazio?",[10,185,186],{},"Geralmente de um de três lugares no código do seu próprio app, e a coluna da\nmensagem diz onde olhar primeiro. Outros dois começam dentro do banco.",[10,188,189,193,194,196,197,200],{},[190,191,192],"strong",{},"Um campo que seu app nunca envia."," É o caso mais comum. A coluna virou\nobrigatória depois que o formulário foi feito: um construtor com IA adicionou\n",[21,195,29],{}," à tabela quando você pediu categorias, marcou como obrigatória e\ndeixou como estava o formulário que salva as tarefas. Nada na requisição nomeia a\ncoluna, a coluna não tem valor padrão, então ela fica vazia. Compare as colunas no\nseu Table Editor com o objeto que seu app passa para ",[21,198,199],{},".insert()",". A que falta é a\nda mensagem.",[10,202,203,204,207,208,210,211,215],{},"Uma chave escrita diferente da coluna nem chega até aqui. O PostgREST recusa\n",[21,205,206],{},"categoryId"," enviado para uma coluna chamada ",[21,209,29],{}," antes que o Postgres\nveja a linha, e\n",[91,212,214],{"href":213},"\u002Fblog\u002Fsupabase-schema-cache-column-error","esse erro tem um artigo próprio",".",[10,217,218,221,222,225,226,229,230,237,238,240],{},[190,219,220],{},"Um null que seu código envia de propósito."," ",[21,223,224],{},"{ category_id: selected?.id ?? null }","\nmanda um null explícito quando nada está selecionado, e um null explícito é um\nvalor para a requisição. O ",[21,227,228],{},"undefined"," do JavaScript se comporta de outro jeito.\nJSON não tem esse valor, então o\n",[91,231,234],{"href":232,"rel":233},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FJavaScript\u002FReference\u002FGlobal_Objects\u002FJSON\u002Fstringify",[95],[21,235,236],{},"JSON.stringify","\no deixa \"omitted (when found in an object)\", e uma chave com ",[21,239,228],{}," nunca\nchega ao Supabase. Qual dos dois seu código produz decide se um valor padrão pode\nentrar, e a próxima seção trata disso.",[10,242,243,221,246,249,250,253,254,257],{},[190,244,245],{},"Uma coluna sem o valor padrão que você supunha.",[21,247,248],{},"created_at"," costuma se\npreencher sozinha, porque a tabela diz ",[21,251,252],{},"default now()",". ",[21,255,256],{},"user_id"," normalmente não,\nporque nada no Postgres sabe quem está logado até alguém dizer à coluna como\ndescobrir. Uma seção mais adiante mostra como.",[10,259,260,263,264,267,268,271,272,277,278,281,282,284,285,289,290,292],{},[190,261,262],{},"Um cadastro, sob outra mensagem."," Um trigger que copia cada usuário novo para\numa tabela ",[21,265,266],{},"profiles"," lê os dados do cadastro com ",[21,269,270],{},"->>",", e\n",[91,273,276],{"href":274,"rel":275},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Ffunctions-json.html",[95],"os operadores JSON do Postgres","\n\"return NULL, rather than failing, if the JSON input does not have the right\nstructure to match the request; for example if no such key or array element\nexists\". Um formulário de cadastro que nunca mandou ",[21,279,280],{},"full_name"," dá ao trigger um\nnome vazio, uma coluna obrigatória ",[21,283,280],{}," recusa, e o Supabase reporta o\ncadastro inteiro como\n",[91,286,288],{"href":287},"\u002Fblog\u002Fdatabase-error-saving-new-user","\"Database error saving new user\"",". O\n",[21,291,82],{}," em si está nos logs do Postgres, e esse artigo mostra onde.",[10,294,295,298,299,302,303,307],{},[190,296,297],{},"Uma exclusão em outro lugar."," Uma chave estrangeira com ",[21,300,301],{},"on delete set null","\nesvazia a coluna em cada linha que apontava para a excluída. Se essa coluna também\né obrigatória, quem falha com esta mensagem é a exclusão, e\n",[91,304,306],{"href":305},"\u002Fblog\u002Fdatabase-error-deleting-user","o artigo sobre excluir usuários"," explica como\nescolher uma regra que sirva.",[39,309,311],{"id":310},"mas-a-coluna-tem-um-valor-padrão","\"Mas a coluna tem um valor padrão\"",[10,313,314,315,320],{},"Então a requisição nomeou a coluna e a mandou vazia. O Postgres aplica um valor\npadrão só quando a coluna fica totalmente de fora da linha.\n",[91,316,319],{"href":317,"rel":318},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fddl-default.html",[95],"A documentação dele sobre valores padrão","\ndiz: \"When a new row is created and no values are specified for some of the\ncolumns, those columns will be filled with their respective default values.\" Um\nnull explícito conta como valor informado. No formulário, um campo com valor\npré-impresso conta como preenchido até alguém escrever \"nenhum\" por cima da\nimpressão.",[10,322,323,324,329,330,333,334,336,337,340,341,346,347,349],{},"O supabase-js escreve \"nenhum\" por cima num lugar que você não esperaria: uma\ninserção em lote, em que você passa um array de linhas. Ele nomeia todas as\ncolunas que aparecem em qualquer uma das linhas, e\n",[91,325,328],{"href":326,"rel":327},"https:\u002F\u002Fgithub.com\u002Fsupabase\u002Fsupabase-js\u002Fblob\u002Fmaster\u002Fpackages\u002Fcore\u002Fpostgrest-js\u002Fsrc\u002FPostgrestQueryBuilder.ts",[95],"o código-fonte dele","\ndocumenta a opção que decide o que acontece com as lacunas, ",[21,331,332],{},"defaultToNull",":\n\"Make missing fields default to ",[21,335,127],{},". Otherwise, use the default value for the\ncolumn. Only applies for bulk inserts.\" Ela vale ",[21,338,339],{},"true"," enquanto você não mudar.\n",[91,342,345],{"href":343,"rel":344},"https:\u002F\u002Fdocs.postgrest.org\u002Fen\u002Fstable\u002Freferences\u002Fapi\u002Fpreferences.html",[95],"A documentação do PostgREST","\ndiz o mesmo para uma requisição que lista as próprias colunas, que é o que o\nsupabase-js manda com um array: \"any missing columns in the payload will be\ninserted as ",[21,348,127],{}," value by default.\"",[10,351,352,353,356,357,359,360,363,364,366],{},"Então aqui a segunda tarefa entra com ",[21,354,355],{},"status"," vazio, mesmo que a tabela dê a\n",[21,358,355],{}," o valor padrão ",[21,361,362],{},"'todo'",", e um ",[21,365,355],{}," obrigatório recusa a inserção\ninteira:",[14,368,373],{"className":369,"code":371,"language":372,"meta":23},[370],"language-js","await supabase.from('tasks').insert([\n  { title: 'Escrever o briefing', status: 'doing' },\n  { title: 'Enviar a fatura' },\n])\n","js",[21,374,371],{"__ignoreMap":23},[10,376,377,378,380],{},"Qualquer uma das duas correções funciona. Mande ",[21,379,355],{}," em todas as linhas, ou\ndiga ao supabase-js para deixar valer os valores padrão da tabela:",[14,382,385],{"className":383,"code":384,"language":372,"meta":23},[370],"await supabase.from('tasks').insert(rows, { defaultToNull: false })\n",[21,386,384],{"__ignoreMap":23},[10,388,389,390,393],{},"Essa opção acrescenta o cabeçalho ",[21,391,392],{},"Prefer: missing=default"," que o PostgREST lê.\nUma única linha passada como objeto, sem o array em volta, não faz isso: uma chave\nque falta nela falta na inserção, e o valor padrão se aplica.",[395,396],"diagram",{"alt":397,"caption":398,"src":399},"A mesma inserção de duas linhas desenhada duas vezes. O segundo formulário deixa de fora o campo status com asterisco. À esquerda, com defaultToNull: true, esse campo mostra null em âmbar sobre o valor pré-impresso e a inserção termina em 23502. À direita, com defaultToNull: false, o valor pré-impresso aparece e a inserção recebe um visto.","Numa inserção em lote, uma coluna que uma linha tem e outra não entra como null na que não tem. defaultToNull: false deixa o valor padrão da tabela preencher.","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fnull-over-the-default-1600x720.png",[39,401,403],{"id":402},"por-que-um-upsert-falha-com-isso-se-a-linha-já-existe","Por que um upsert falha com isso se a linha já existe?",[10,405,406],{},"Porque o Postgres confere se a linha nova tem colunas obrigatórias vazias antes de\nprocurar a linha existente que atualizaria. Um upsert é um formulário marcado\n\"arquive, ou, se já houver uma ficha dessa pessoa, altere\". O Postgres confere os\nasteriscos primeiro. Um formulário que traz só uma foto de perfil nova volta,\nmesmo com a ficha que ele devia alterar parada na gaveta.",[10,408,409,410,415,416,419],{},"A ordem está escrita\n",[91,411,414],{"href":412,"rel":413},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Fexecutor\u002FnodeModifyTable.c",[95],"no código que executa uma inserção",":\n\"Check the constraints of the tuple.\" vem antes de \"Perform a speculative\ninsertion.\", que é o passo que encontra o conflito ao qual uma cláusula\n",[21,417,418],{},"on conflict"," responde. Tuple é a palavra do Postgres para linha. Então este\nupsert, feito para mudar uma coluna de um perfil que existe, falha em qualquer\noutra coluna que a tabela exija:",[14,421,424],{"className":422,"code":423,"language":372,"meta":23},[370],"await supabase.from('profiles').upsert({ id: user.id, avatar_url: url })\n",[21,425,423],{"__ignoreMap":23},[10,427,428],{},"Para mudar algumas colunas de uma linha que já está lá, use um update. Ele cai\nsobre a linha gravada, que tem todas as colunas obrigatórias preenchidas, e muda\nsó o que você envia:",[14,430,433],{"className":431,"code":432,"language":372,"meta":23},[370],"await supabase.from('profiles').update({ avatar_url: url }).eq('id', user.id)\n",[21,434,432],{"__ignoreMap":23},[10,436,437,438,442],{},"Deixe o upsert para linhas que talvez ainda não existam, e mande junto todas as\ncolunas obrigatórias.\n",[91,439,441],{"href":440},"\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint","O artigo sobre chaves duplicadas","\ncobre o resto do que um upsert precisa: em qual coluna ele compara e quais regras\nde leitura ele aciona a partir do navegador.",[395,444],{"alt":445,"caption":446,"src":447},"Duas faixas que começam com a mesma mudança de um campo. Na faixa do upsert ela vira uma linha nova cujo segundo campo com asterisco está vazio, uma cancela com asterisco se fecha em âmbar com 23502 embaixo, e a cancela on conflict e a gaveta adiante nunca são alcançadas. Na faixa do update ela cai sobre a linha gravada com todos os campos com asterisco preenchidos, a cancela está aberta e a gaveta a recebe.","A linha de um upsert é conferida em busca de colunas obrigatórias vazias antes de o Postgres procurar a linha que atualizaria. Um update parte da linha gravada, que já tem todas.","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fupsert-is-checked-first-1600x780.png",[39,449,451],{"id":450},"você-deve-remover-a-regra-not-null","Você deve remover a regra NOT NULL?",[10,453,454],{},"Só se a coluna nunca devia ter sido obrigatória. Um telefone que seu formulário\ntrata como opcional é esse caso, e ali remover a regra é a correção certa:",[14,456,461],{"className":457,"code":459,"language":460,"meta":23},[458],"language-sql","alter table public.profiles alter column phone drop not null;\n","sql",[21,462,459],{"__ignoreMap":23},[10,464,465,466,469,470,473],{},"Para qualquer outra coluna, mantenha a regra e preencha o campo. Sem ela, cada\nlinha salva depois pode ficar sem o valor que a regra garantia, e seu app encontra\na lacuna em outro lugar: uma página que lê ",[21,467,468],{},"task.category.name"," para com\n",[21,471,472],{},"Cannot read properties of null",", ou uma contagem de tarefas por categoria perde\nem silêncio as que não têm nenhuma.",[10,475,476,477,479,480,483],{},"A coluna em que remover sai mais caro é ",[21,478,256],{},". Uma linha salva com dono vazio\nnão aparece para ninguém. Uma regra de leitura escrita como ",[21,481,482],{},"auth.uid() = user_id","\nnunca bate com ela, porque em SQL um valor vazio não é igual a nada, nem a outro\nvalor vazio. A tarefa fica salva, e daí em diante ninguém a vê, nem a pessoa que a\ncriou.",[10,485,486,487,489],{},"A correção para ",[21,488,256],{}," é um valor padrão que preenche quem estiver logado:",[14,491,494],{"className":492,"code":493,"language":460,"meta":23},[458],"alter table public.tasks\n  alter column user_id set default auth.uid();\n",[21,495,493],{"__ignoreMap":23},[10,497,498,499,503,504,507,508,513],{},"O Postgres avalia um valor padrão \"whenever the default value is inserted (not\nwhen the table is created)\", nas\n",[91,500,502],{"href":317,"rel":501},[95],"palavras dele",", então\ncada linha nova recebe o id de quem está salvando. Sem ninguém logado,\n",[21,505,506],{},"auth.uid()"," retorna null, como avisa\n",[91,509,512],{"href":510,"rel":511},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Fdatabase\u002Fpostgres\u002Frow-level-security",[95],"o guia do Supabase sobre row level security",",\ne o salvamento continua falhando. É a regra fazendo o trabalho dela. O valor\npadrão preenche o campo só quando seu app o deixa de fora, então o que impede\nalguém de mandar o id de outra pessoa continua sendo a sua regra de inserção.",[10,515,516,517,521,522,524,525,527,528,530,531,535,536,538,539,541,542,544],{},"Essa regra de inserção também muda a mensagem que você vê. O Postgres roda as\nverificações de Row Level Security de uma tabela numa linha nova antes das\nverificações not-null:\n",[91,518,520],{"href":412,"rel":519},[95],"o mesmo código de inserção","\npõe \"Check any RLS WITH CHECK policies.\" antes de \"Check the constraints of the\ntuple.\". Quando a sua regra de inserção compara ",[21,523,256],{}," com ",[21,526,506],{},", um\n",[21,529,256],{}," vazio falha primeiro na regra e volta como\n",[91,532,534],{"href":533},"\u002Fblog\u002Fnew-row-violates-row-level-security-policy","new row violates row-level security policy",".\nUm ",[21,537,82],{}," em ",[21,540,256],{}," vindo do seu app significa uma de três coisas: a regra de\ninserção nunca olha ",[21,543,256],{},", as regras estão desligadas nessa tabela, ou a\nrequisição veio de código de servidor que as pula.",[39,546,548],{"id":547},"é-seguro-mostrar-esse-erro-aos-seus-usuários","É seguro mostrar esse erro aos seus usuários?",[10,550,551],{},"Não. Mostre uma frase sua e guarde o erro cru nos seus logs, porque a linha de\ndetalhe, quando o Postgres a inclui, é uma cópia da linha inteira.",[10,553,554,555,559,560,562],{},"A linha de detalhe de uma chave duplicada repete um valor. Esta repete o\nformulário.\n",[91,556,558],{"href":176,"rel":557},[95],"A função que a escreve","\nmostra a linha como ",[21,561,123],{},", com cada coluna que quem chamou\npode ler. Num update, é a linha como teria sido gravada, incluindo as colunas que\nninguém tocou. Um formulário que o Postgres devolve pode vir com uma fotocópia de\nsi mesmo grampeada na recusa, e a cópia mostra todos os campos.",[10,564,565,566,569],{},"O Postgres decide quem recebe a fotocópia, e o código dele confere primeiro:\n\"Check if RLS is enabled and should be active for the relation; if so, then don't\nreturn anything.\" Uma requisição feita com a sua chave pública a uma tabela com\nRow Level Security ligada recebe ",[21,567,568],{},"\"details\": null",", que é o que o console no topo\ndesta página mostrou.",[10,571,572],{},"Dois chamadores recebem a cópia completa.",[10,574,575,578,579,582,583,586,587,591,592,596],{},[190,576,577],{},"Um navegador, quando as regras da tabela estão desligadas."," Se o console mostra\n",[21,580,581],{},"Failing row contains"," para uma requisição que seu app fez com a chave pública, a\nRow Level Security está desligada nessa tabela. É a mesma leitura que\n",[91,584,585],{"href":440},"o artigo sobre chaves duplicadas","\nfaz da própria linha de detalhe, e\n",[91,588,590],{"href":589},"\u002Fblog\u002Fenable-rls-on-every-supabase-table","ligar as regras em todas as tabelas","\ncobre o interruptor e a primeira regra. Uma tabela com as regras desligadas\nentrega as linhas a um estranho tão facilmente quanto a você. Nossa\n",[91,593,595],{"href":594},"\u002Fsecurity-scanner","varredura gratuita"," lê seu app no ar com a chave pública que\nqualquer visitante recebe e lista as tabelas que devolvem linhas. Não precisa de\nconta.",[10,598,599,602,603,606,607,610,611,614],{},[190,600,601],{},"Código de servidor que guarda a chave secreta."," O guia do Supabase diz que a\nchave secreta \"authorizes access through the ",[21,604,605],{},"service_role"," Postgres role, which\nhas the ",[21,608,609],{},"bypassrls"," attribute\", então as regras nunca se aplicam e a linha de\ndetalhe vem sempre preenchida. Uma Edge Function que atualiza uma linha e responde\ncom o erro que recebeu, ",[21,612,613],{},"return new Response(JSON.stringify(error))",", manda ao\nnavegador todas as colunas da linha que estava mudando, seja de quem for essa\nlinha.",[10,616,617],{},"No código de servidor, guarde o erro e responda com suas próprias palavras:",[14,619,622],{"className":620,"code":621,"language":372,"meta":23},[370],"if (error) {\n  console.error(error)\n  return new Response(JSON.stringify({ message: 'Não foi possível salvar.' }), {\n    status: 400,\n  })\n}\n",[21,623,621],{"__ignoreMap":23},[10,625,626],{},"No app, capture o código e diga o que a pessoa pode fazer:",[14,628,631],{"className":629,"code":630,"language":372,"meta":23},[370],"const { error } = await supabase.from('tasks').insert(task)\n\nif (error?.code === '23502') {\n  setMessage('Escolha uma categoria antes de salvar.')\n} else if (error) {\n  setMessage('Algo deu errado. Tente de novo.')\n}\n",[21,632,630],{"__ignoreMap":23},[10,634,635,639],{},[91,636,638],{"href":637},"\u002Fblog\u002Fmove-a-secret-to-an-edge-function","Levar uma chave secreta para uma Edge Function","\ncobre a parte da função, se a chave secreta ainda estiver no seu app.",[395,641],{"alt":642,"caption":643,"src":644},"A mesma linha recusada enviada duas vezes para a mesma gaveta. À esquerda, de uma janela de navegador, a gaveta tem um cadeado e a resposta diz code 23502, 400 e details null. À direita, de uma função que segura uma chave, a gaveta não tem cadeado e a linha details da resposta, Failing row contains, está em âmbar, com uma fileira de barras âmbar embaixo, uma por coluna.","Com Row Level Security ligada, o navegador não recebe cópia da linha. Código de servidor com a chave secreta recebe a linha inteira, e a repassa se devolver o erro como veio.","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fthe-whole-row-comes-back-1600x760.png",[39,646,648],{"id":647},"antes-de-mudar-as-regras-de-uma-coluna","Antes de mudar as regras de uma coluna",[10,650,651],{},"Guarde uma cópia do banco de antes de acrescentar um valor padrão, remover uma\nregra ou preencher um valor em todas as linhas, e fique sabendo quando uma tabela\ncomeçar a responder a estranhos.",[10,653,654],{},[190,655,656,660],{},[91,657,659],{"href":658},"\u002Fsupabase-backups","Reeve Care"," guarda uma cópia do seu banco no Supabase:",[662,663,664,668,671,674,677,680],"ul",{},[665,666,667],"li",{},"uma cópia criptografada num cronograma, no armazenamento próprio da Reeve na UE, fora da sua conta do Supabase",[665,669,670],{},"cada linha como estava antes de um update na tabela inteira reescrevê-la",[665,672,673],{},"cada cópia verificada antes de valer, contando as linhas de cada tabela",[665,675,676],{},"seus arquivos enviados também, assim que você conecta uma credencial do Storage",[665,678,679],{},"uma restauração com um clique, com uma cópia de segurança do estado atual feita antes",[665,681,682],{},"tudo o que o Monitor faz",[10,684,685],{},[190,686,687,691],{},[91,688,690],{"href":689},"\u002Fwebsite-monitoring","Reeve Monitor"," continua conferindo de fora:",[662,693,694,697,700,703],{},[665,695,696],{},"se seu app está no ar, conferido a cada minuto",[665,698,699],{},"a verificação de segurança completa a cada hora, em até três apps",[665,701,702],{},"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",[665,704,705],{},"um relatório mensal, em linguagem simples, do que ele viu",[39,707,709],{"id":708},"o-que-fazer-agora","O que fazer agora",[711,712,713],"key-takeaways",{},[662,714,715,718,724,731,738,746],{},[665,716,717],{},"Leia a coluna e a tabela na mensagem. É o campo que chegou vazio.",[665,719,720,721,723],{},"Confira se seu app deixa esse campo de fora, manda como ",[21,722,127],{}," ou espera um valor padrão que a coluna não tem.",[665,725,726,727,730],{},"Para mudar algumas colunas de uma linha que existe, use ",[21,728,729],{},".update()"," com um filtro. Um upsert tem que levar todas as colunas obrigatórias.",[665,732,733,734,737],{},"Numa inserção em lote, mande todas as colunas em todas as linhas, ou passe ",[21,735,736],{},"{ defaultToNull: false }"," para os valores padrão da tabela valerem.",[665,739,740,741,743,744,215],{},"Mantenha a regra em ",[21,742,256],{}," e dê à coluna o valor padrão ",[21,745,506],{},[665,747,748],{},"No código de servidor, registre o erro cru no log e responda ao navegador com uma frase sua.",[10,750,751,752,755],{},"Comece pela coluna da mensagem e depois pela linha do seu código que monta a\nlinha. Se a correção muda a própria tabela, um valor padrão novo ou um update em\ntodas as linhas, ",[91,753,754],{"href":658},"guarde uma cópia do banco"," de antes de\nrodar.",{"title":23,"searchDepth":757,"depth":757,"links":758},3,[759,761,762,763,764,765,766,767],{"id":41,"depth":760,"text":42},2,{"id":182,"depth":760,"text":183},{"id":310,"depth":760,"text":311},{"id":402,"depth":760,"text":403},{"id":450,"depth":760,"text":451},{"id":547,"depth":760,"text":548},{"id":647,"depth":760,"text":648},{"id":708,"depth":760,"text":709},"Noções de segurança","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fcover-1200x630.png","Um formulário de papel na frente de uma gaveta de arquivo aberta, com o segundo campo com asterisco vazio e contornado em âmbar.","Supabase \"null value in column violates not-null constraint\": uma coluna obrigatória chegou vazia. De onde veio o null, e como corrigir.",false,"md",[775,778,781,784,787],{"q":776,"a":777},"O que significa null value in column violates not-null constraint?","Significa que seu app tentou salvar uma linha sem valor numa coluna que sempre precisa ter um, e o Postgres recusou a linha inteira. Nada foi gravado. A mensagem nomeia a coluna e a tabela. O código é 23502, e de um app Supabase ele chega como HTTP 400.",{"q":779,"a":780},"Por que a coluna é null se ela tem um valor padrão?","Porque a requisição nomeou a coluna e a mandou vazia. O Postgres aplica um valor padrão só quando a coluna é deixada de fora. O supabase-js faz exatamente isso numa inserção em lote: uma coluna que uma linha tem e outra não entra como null na que não tem, a menos que você passe defaultToNull: false.",{"q":782,"a":783},"Por que meu upsert falha com um erro not-null se a linha existe?","O Postgres confere se a linha nova tem colunas obrigatórias vazias antes de procurar a linha existente que atualizaria. Um upsert que leva só as colunas que você quer mudar falha em qualquer outra coluna obrigatória. Para uma linha que já existe, use update com um filtro.",{"q":785,"a":786},"Devo remover a restrição NOT NULL?","Só se a coluna nunca devia ter sido obrigatória, como um telefone que seu formulário trata como opcional. Do contrário o erro para e linhas começam a chegar sem o valor, que seu app encontra em outro lugar. Uma linha com user_id vazio não aparece para ninguém. Descubra de onde vem o null, ou dê à coluna um valor padrão como auth.uid().",{"q":788,"a":789},"É seguro mostrar esse erro aos usuários?","Não. Quando o Postgres inclui a linha de detalhe, ela é uma cópia da linha inteira. Uma requisição do navegador a uma tabela com Row Level Security ligada não recebe nenhuma, mas código de servidor com a chave secreta recebe sempre. Capture o código 23502, mostre uma frase escrita por você e guarde o erro cru nos seus logs.","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fcard-800x500.png",[792,793,794,795,796,797,798],"null value in column violates not-null constraint","erro not null supabase","postgres 23502","failing row contains","supabase upsert not null","null value in column user_id violates not-null constraint","supabase defaulttonull",{},true,"\u002Fblog\u002Fnull-value-violates-not-null-constraint","2026-11-06",{"title":5,"description":771},"blog\u002Fnull-value-violates-not-null-constraint",[806,807,808],"Supabase \"null value in column violates not-null constraint\" (código do Postgres 23502, HTTP 400 no seu app) significa que uma linha chegou sem nada numa coluna que sempre precisa ter um valor. A mensagem nomeia a coluna e a tabela. Nada foi salvo.","O valor vazio vem de um campo que seu app nunca envia, de um null que seu código envia de propósito ou de uma coluna sem o valor padrão que você supunha. Um upsert e uma inserção em lote podem produzi-lo numa linha que parece completa.","Mantenha a regra e corrija a linha. Do código de servidor, nunca devolva o erro cru ao navegador: a linha \"Failing row contains\" dele pode mostrar a linha inteira.","2sZAFgf31LbuNQz7qloObUxfIoORK_oBwnYWUAGAZjQ",[811,817,823,829,830,836,842,848,854,860,866,872,878,884,890,896,902,907,913,919,924,930,936,942,948,954,960,965,971,977,983,989,995,1001,1007,1013,1018,1024,1030,1036,1042,1048,1054,1060,1066,1072,1077,1083,1089,1095,1101,1107,1113,1119,1125,1131,1137,1143,1149,1155,1161,1167,1172,1178,1184,1190,1196,1202,1208,1214,1220,1226,1232,1238,1244,1250,1255,1261,1267,1273,1279,1285,1291,1297,1302,1308,1314,1320,1325,1331],{"path":812,"title":813,"description":814,"published":815,"category":768,"image":816,"draft":772},"\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":818,"title":819,"description":820,"published":821,"category":768,"image":822,"draft":772},"\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":824,"title":825,"description":826,"published":827,"category":768,"image":828,"draft":772},"\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":801,"title":5,"description":771,"published":802,"category":768,"image":790,"draft":772},{"path":831,"title":832,"description":833,"published":834,"category":768,"image":835,"draft":772},"\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":837,"title":838,"description":839,"published":840,"category":768,"image":841,"draft":772},"\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":843,"title":844,"description":845,"published":846,"category":768,"image":847,"draft":772},"\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":849,"title":850,"description":851,"published":852,"category":768,"image":853,"draft":772},"\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":855,"title":856,"description":857,"published":858,"category":768,"image":859,"draft":772},"\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":861,"title":862,"description":863,"published":864,"category":768,"image":865,"draft":772},"\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":305,"title":867,"description":868,"published":869,"category":870,"image":871,"draft":772},"\"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":873,"title":874,"description":875,"published":876,"category":768,"image":877,"draft":772},"\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":879,"title":880,"description":881,"published":882,"category":768,"image":883,"draft":772},"\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":885,"title":886,"description":887,"published":888,"category":768,"image":889,"draft":772},"\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":891,"title":892,"description":893,"published":894,"category":768,"image":895,"draft":772},"\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":897,"title":898,"description":899,"published":900,"category":768,"image":901,"draft":772},"\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":440,"title":903,"description":904,"published":905,"category":768,"image":906,"draft":772},"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":908,"title":909,"description":910,"published":911,"category":768,"image":912,"draft":772},"\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":914,"title":915,"description":916,"published":917,"category":768,"image":918,"draft":772},"\u002Fblog\u002Fsupabase-function-not-found","Supabase PGRST202: \"Could not find the function in the schema cache\"","Supabase \"Could not find the function in the schema cache\" 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":213,"title":920,"description":921,"published":922,"category":768,"image":923,"draft":772},"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":925,"title":926,"description":927,"published":928,"category":768,"image":929,"draft":772},"\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":931,"title":932,"description":933,"published":934,"category":768,"image":935,"draft":772},"\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":937,"title":938,"description":939,"published":940,"category":768,"image":941,"draft":772},"\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":943,"title":944,"description":945,"published":946,"category":768,"image":947,"draft":772},"\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":949,"title":950,"description":951,"published":952,"category":768,"image":953,"draft":772},"\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":955,"title":956,"description":957,"published":958,"category":768,"image":959,"draft":772},"\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":287,"title":961,"description":962,"published":963,"category":768,"image":964,"draft":772},"\"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":966,"title":967,"description":968,"published":969,"category":768,"image":970,"draft":772},"\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":972,"title":973,"description":974,"published":975,"category":768,"image":976,"draft":772},"\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":978,"title":979,"description":980,"published":981,"category":768,"image":982,"draft":772},"\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":984,"title":985,"description":986,"published":987,"category":768,"image":988,"draft":772},"\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":990,"title":991,"description":992,"published":993,"category":768,"image":994,"draft":772},"\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":996,"title":997,"description":998,"published":999,"category":768,"image":1000,"draft":772},"\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":1002,"title":1003,"description":1004,"published":1005,"category":768,"image":1006,"draft":772},"\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":1008,"title":1009,"description":1010,"published":1011,"category":768,"image":1012,"draft":772},"\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":637,"title":1014,"description":1015,"published":1016,"category":768,"image":1017,"draft":772},"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":1019,"title":1020,"description":1021,"published":1022,"category":768,"image":1023,"draft":772},"\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":1025,"title":1026,"description":1027,"published":1028,"category":768,"image":1029,"draft":772},"\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":1031,"title":1032,"description":1033,"published":1034,"category":768,"image":1035,"draft":772},"\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":1037,"title":1038,"description":1039,"published":1040,"category":768,"image":1041,"draft":772},"\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":1043,"title":1044,"description":1045,"published":1046,"category":768,"image":1047,"draft":772},"\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":1049,"title":1050,"description":1051,"published":1052,"category":768,"image":1053,"draft":772},"\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":1055,"title":1056,"description":1057,"published":1058,"category":870,"image":1059,"draft":772},"\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":1061,"title":1062,"description":1063,"published":1064,"category":870,"image":1065,"draft":772},"\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":1067,"title":1068,"description":1069,"published":1070,"category":870,"image":1071,"draft":772},"\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":1073,"title":1074,"description":1075,"published":1070,"category":768,"image":1076,"draft":772},"\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":1078,"title":1079,"description":1080,"published":1081,"category":768,"image":1082,"draft":772},"\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":1084,"title":1085,"description":1086,"published":1087,"category":768,"image":1088,"draft":772},"\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":1090,"title":1091,"description":1092,"published":1093,"category":870,"image":1094,"draft":772},"\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":1096,"title":1097,"description":1098,"published":1099,"category":768,"image":1100,"draft":772},"\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":1102,"title":1103,"description":1104,"published":1105,"category":768,"image":1106,"draft":772},"\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":1108,"title":1109,"description":1110,"published":1111,"category":768,"image":1112,"draft":772},"\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":1114,"title":1115,"description":1116,"published":1117,"category":768,"image":1118,"draft":772},"\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":1120,"title":1121,"description":1122,"published":1123,"category":768,"image":1124,"draft":772},"\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":1126,"title":1127,"description":1128,"published":1129,"category":768,"image":1130,"draft":772},"\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":1132,"title":1133,"description":1134,"published":1135,"category":768,"image":1136,"draft":772},"\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":1138,"title":1139,"description":1140,"published":1141,"category":768,"image":1142,"draft":772},"\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":1144,"title":1145,"description":1146,"published":1147,"category":870,"image":1148,"draft":772},"\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":1150,"title":1151,"description":1152,"published":1153,"category":870,"image":1154,"draft":772},"\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":1156,"title":1157,"description":1158,"published":1159,"category":870,"image":1160,"draft":772},"\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":1162,"title":1163,"description":1164,"published":1165,"category":768,"image":1166,"draft":772},"\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":589,"title":1168,"description":1169,"published":1170,"category":768,"image":1171,"draft":772},"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":1173,"title":1174,"description":1175,"published":1176,"category":768,"image":1177,"draft":772},"\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":1179,"title":1180,"description":1181,"published":1182,"category":768,"image":1183,"draft":772},"\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":1185,"title":1186,"description":1187,"published":1188,"category":768,"image":1189,"draft":772},"\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":1191,"title":1192,"description":1193,"published":1194,"category":768,"image":1195,"draft":772},"\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":1197,"title":1198,"description":1199,"published":1200,"category":768,"image":1201,"draft":772},"\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":1203,"title":1204,"description":1205,"published":1206,"category":768,"image":1207,"draft":772},"\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":1209,"title":1210,"description":1211,"published":1212,"category":768,"image":1213,"draft":772},"\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":1215,"title":1216,"description":1217,"published":1218,"category":768,"image":1219,"draft":772},"\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":1221,"title":1222,"description":1223,"published":1224,"category":870,"image":1225,"draft":772},"\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":1227,"title":1228,"description":1229,"published":1230,"category":768,"image":1231,"draft":772},"\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":1233,"title":1234,"description":1235,"published":1236,"category":870,"image":1237,"draft":772},"\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":1239,"title":1240,"description":1241,"published":1242,"category":768,"image":1243,"draft":772},"\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":1245,"title":1246,"description":1247,"published":1248,"category":768,"image":1249,"draft":772},"\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":533,"title":1251,"description":1252,"published":1253,"category":768,"image":1254,"draft":772},"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":1256,"title":1257,"description":1258,"published":1259,"category":768,"image":1260,"draft":772},"\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":1262,"title":1263,"description":1264,"published":1265,"category":870,"image":1266,"draft":772},"\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":1268,"title":1269,"description":1270,"published":1271,"category":870,"image":1272,"draft":772},"\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":1274,"title":1275,"description":1276,"published":1277,"category":768,"image":1278,"draft":772},"\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":1280,"title":1281,"description":1282,"published":1283,"category":768,"image":1284,"draft":772},"\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":1286,"title":1287,"description":1288,"published":1289,"category":870,"image":1290,"draft":772},"\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":1292,"title":1293,"description":1294,"published":1295,"category":768,"image":1296,"draft":772},"\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":1298,"title":1299,"description":1300,"published":1295,"category":768,"image":1301,"draft":772},"\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":1303,"title":1304,"description":1305,"published":1306,"category":768,"image":1307,"draft":772},"\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":1309,"title":1310,"description":1311,"published":1312,"category":870,"image":1313,"draft":772},"\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":1315,"title":1316,"description":1317,"published":1318,"category":768,"image":1319,"draft":772},"\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":1321,"title":1322,"description":1323,"published":1318,"category":870,"image":1324,"draft":772},"\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":1326,"title":1327,"description":1328,"published":1329,"category":870,"image":1330,"draft":772},"\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":1332,"title":1333,"description":1334,"published":1329,"category":768,"image":1335,"draft":772},"\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",1794213648644]