[{"data":1,"prerenderedAt":1385},["ShallowReactive",2],{"blog-es-supabase-relation-does-not-exist":3,"blog-index-es":859},{"id":4,"title":5,"body":6,"category":815,"cover":816,"coverAlt":817,"description":818,"draft":819,"extension":820,"faq":821,"image":837,"keywords":838,"meta":848,"navigation":849,"ogTitle":5,"path":850,"published":851,"seo":852,"stem":853,"tldr":854,"updated":851,"__hash__":858},"blog_es\u002Fblog\u002Fsupabase-relation-does-not-exist.md","Supabase \"relation does not exist\" aunque la tabla está ahí",{"type":7,"value":8,"toc":802},"minimark",[9,18,28,35,38,43,61,72,75,148,181,192,196,199,208,245,302,314,317,325,336,340,353,356,403,419,425,438,443,449,453,456,464,470,484,491,494,500,517,520,526,534,539,543,549,552,558,565,580,584,597,609,612,618,625,643,648,652,655,666,675,679,682,692,715,724,742,746,795],[10,11,12,13,17],"p",{},"Tu builder añadió una tabla ",[14,15,16],"code",{},"invoices",". La ves en el Table Editor, con sus filas, y\nla página que debería listarlas se queda vacía. La consola del navegador muestra\nesto:",[19,20,25],"pre",{"className":21,"code":23,"language":24},[22],"language-text","{\n  \"code\": \"PGRST205\",\n  \"details\": null,\n  \"hint\": \"Perhaps you meant the table 'public.Invoices'\",\n  \"message\": \"Could not find the table 'public.invoices' in the schema cache\"\n}\n","text",[14,26,23],{"__ignoreMap":27},"",[10,29,30,31,34],{},"Supabase \"relation does not exist\" significa que Postgres no encontró ninguna\ntabla ni vista con ese nombre exacto en los esquemas que recorrió la consulta.\nPGRST205 es la misma respuesta dada por PostgREST, la parte de Supabase que\nconvierte las llamadas ",[14,32,33],{},"supabase.from(...)"," de tu app en consultas a la base de\ndatos, antes incluso de que la consulta llegue a Postgres.",[10,36,37],{},"La solución que circula para este error es añadir el esquema a Exposed schemas en\nlos ajustes del proyecto. Eso arregla una de las cuatro causas de abajo. También\ndeja que cualquiera con la clave pública de tu app llegue a todas las tablas de\nese esquema, así que lee lo que abre antes de activarlo.",[39,40,42],"h2",{"id":41},"qué-significa-relation-does-not-exist-en-supabase","¿Qué significa \"relation does not exist\" en Supabase?",[10,44,45,46,49,50,60],{},"La base de datos buscó una tabla con ese nombre exacto, en los esquemas que le\ndijeron que recorriera, y no encontró ninguna. \"Relation\" es la palabra de Postgres\npara cualquier cosa de la que salen filas, una tabla o una vista. El código del\nerror es ",[14,47,48],{},"42P01",", al que\n",[51,52,56,57],"a",{"href":53,"rel":54},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Futils\u002Ferrcodes.txt",[55],"nofollow","Postgres llama ",[14,58,59],{},"undefined_table",".",[10,62,63,64,67,68,71],{},"Piensa en tu base de datos como un edificio. Cada esquema es una planta: ",[14,65,66],{},"public","\nes la planta baja, y ",[14,69,70],{},"auth",", donde Supabase guarda a tus usuarios, es otra. Cada\ntabla es una sala con su nombre en la puerta. Una consulta que da solo el nombre de\nuna sala la busca en las plantas que figuran en su ruta de búsqueda. Las\npeticiones de tu app suben en un ascensor, y el ascensor solo para en las plantas\nque tu proyecto expone. El error significa que nadie encontró una puerta con ese\nnombre en las plantas que revisó.",[10,73,74],{},"La redacción que ves depende de dónde se ejecutó la consulta:",[76,77,78,91],"table",{},[79,80,81],"thead",{},[82,83,84,88],"tr",{},[85,86,87],"th",{},"Dónde se ejecutó la consulta",[85,89,90],{},"Qué vuelve",[92,93,94,108,122,134],"tbody",{},[82,95,96,100],{},[97,98,99],"td",{},"El SQL Editor, una migración, una función o un trigger",[97,101,102,105,106],{},[14,103,104],{},"relation \"invoices\" does not exist",", código ",[14,107,48],{},[82,109,110,113],{},[97,111,112],{},"Tu app, con PostgREST 13 o posterior",[97,114,115,105,118,121],{},[14,116,117],{},"Could not find the table 'public.invoices' in the schema cache",[14,119,120],{},"PGRST205",", HTTP 404",[82,123,124,127],{},[97,125,126],{},"Tu app, con un PostgREST más antiguo",[97,128,129,130,133],{},"una lectura recibe ",[14,131,132],{},"relation \"public.invoices\" does not exist",", HTTP 404, y un insert un error vacío",[82,135,136,139],{},[97,137,138],{},"Tu app, pidiendo un esquema que el proyecto no expone",[97,140,141,105,144,147],{},[14,142,143],{},"Invalid schema: billing",[14,145,146],{},"PGRST106",", HTTP 406",[10,149,150,151,156,157,159,160,165,166,168,169,174,175,180],{},"El ",[51,152,155],{"href":153,"rel":154},"https:\u002F\u002Fgithub.com\u002FPostgREST\u002Fpostgrest\u002Fblob\u002Fmain\u002FCHANGELOG.md",[55],"changelog de PostgREST","\nfecha la redacción nueva en la versión 13.0.0, publicada el 8 de mayo de 2025:\n\"Querying non-existent table now returns ",[14,158,120],{}," error\". Antes, como\n",[51,161,164],{"href":162,"rel":163},"https:\u002F\u002Fgithub.com\u002FPostgREST\u002Fpostgrest\u002Fissues\u002F3697",[55],"explicó uno de sus mantenedores en el issue #3697",",\nuna lectura iba a Postgres sin comprobar y volvía como ",[14,167,48],{},". La\n",[51,170,173],{"href":171,"rel":172},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Fdatabase\u002Fhardening-data-api",[55],"guía de Supabase para proteger la Data API","\ndice que puedes consultar la versión de PostgREST de tu proyecto en el Dashboard.\n",[51,176,179],{"href":177,"rel":178},"https:\u002F\u002Fgithub.com\u002FPostgREST\u002Fpostgrest\u002Fblob\u002Fmain\u002Fsrc\u002Flibrary\u002FPostgREST\u002FError.hs",[55],"El propio código de PostgREST","\nfija el resto: la pista de PGRST205, cuando la hay, nombra la tabla más parecida\nque conoce en la misma planta.",[10,182,183,184,187,188,60],{},"Si el mensaje dice en cambio ",[14,185,186],{},"permission denied for table",", la tabla se encontró y\na tu app la rechazaron en la puerta. Ese tiene\n",[51,189,191],{"href":190},"\u002Fblog\u002Fsupabase-permission-denied-for-table","otro arreglo",[39,193,195],{"id":194},"causa-1-el-nombre-de-la-puerta-lleva-una-mayúscula","Causa 1: el nombre de la puerta lleva una mayúscula",[10,197,198],{},"El nombre tiene que coincidir exactamente, mayúsculas incluidas, y dos de las\nherramientas que usas tratan las mayúsculas de forma distinta a la tercera.",[10,200,201,202,207],{},"La\n",[51,203,206],{"href":204,"rel":205},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fsql-syntax-lexical.html",[55],"documentación de Postgres","\nresume la regla en una frase: \"Quoting an identifier also makes it\ncase-sensitive, whereas unquoted names are always folded to lower case.\" Las otras\ndos conservan lo que escribiste:",[209,210,211,227],"ul",{},[212,213,214,215,220,221,224,225],"li",{},"el Table Editor de Supabase crea la tabla con exactamente el nombre que le das.\n",[51,216,219],{"href":217,"rel":218},"https:\u002F\u002Fgithub.com\u002Fsupabase\u002Fsupabase\u002Fblob\u002Fmaster\u002Fpackages\u002Fpg-meta\u002Fsrc\u002Fpg-meta-tables.ts",[55],"Su código","\npone el nombre entre comillas cuando hace falta, así que ",[14,222,223],{},"Invoices"," sigue siendo\n",[14,226,223],{},[212,228,229,230,237,238,241,242,244],{},"PostgREST pone entre comillas dobles cada nombre que envía tu app\n(",[51,231,234],{"href":232,"rel":233},"https:\u002F\u002Fgithub.com\u002FPostgREST\u002Fpostgrest\u002Fblob\u002Fmain\u002Fsrc\u002Flibrary\u002FPostgREST\u002FQuery\u002FSqlFragment.hs",[55],[14,235,236],{},"pgFmtIdent","),\nasí que ",[14,239,240],{},".from('invoices')"," pide ",[14,243,16],{}," y nada más",[76,246,247,260],{},[79,248,249],{},[82,250,251,254,257],{},[85,252,253],{},"Cómo se creó la tabla",[85,255,256],{},"En SQL, escribe",[85,258,259],{},"En tu app, escribe",[92,261,262,281],{},[82,263,264,273,277],{},[97,265,266,269,270,272],{},[14,267,268],{},"create table invoices",", o ",[14,271,223],{}," escrito en SQL sin comillas",[97,274,275],{},[14,276,16],{},[97,278,279],{},[14,280,240],{},[82,282,283,291,297],{},[97,284,285,269,288,290],{},[14,286,287],{},"create table \"Invoices\"",[14,289,223],{}," escrito en el Table Editor",[97,292,293,296],{},[14,294,295],{},"\"Invoices\"",", con comillas",[97,298,299],{},[14,300,301],{},".from('Invoices')",[10,303,304,305,307,308,310,311,313],{},"La segunda fila produce este error por los dos lados. Tu app llama a\n",[14,306,240],{}," y recibe PGRST205, con el nombre real en la pista, como en el\nmensaje del principio de esta página. O la app funciona, y una función o una\npolicy que nombra ",[14,309,223],{}," sin comillas falla con\n",[14,312,104],{},", porque Postgres pasó el nombre a minúsculas\nantes de buscar.",[10,315,316],{},"Para ver todas las tablas con ese nombre, en cualquier planta y con cualquier\ncombinación de mayúsculas, ejecuta esto en el SQL Editor:",[19,318,323],{"className":319,"code":321,"language":322,"meta":27},[320],"language-sql","select table_schema, table_name\nfrom information_schema.tables\nwhere table_name ilike 'invoices';\n","sql",[14,324,321],{"__ignoreMap":27},[10,326,327,328,330,331,335],{},"El arreglo que no toca nada de tus datos está en el código: escribe ",[14,329,295],{},"\ncon sus comillas en SQL, y las mayúsculas exactas en la app. Renombrar la tabla en\nsu lugar rompe cada pantalla que todavía use el nombre antiguo.\n",[51,332,334],{"href":333},"\u002Fblog\u002Fsupabase-schema-cache-column-error","La versión de este error para columnas","\ntiene la misma trampa con los nombres de columna.",[39,337,339],{"id":338},"causa-2-la-tabla-está-en-otra-planta","Causa 2: la tabla está en otra planta",[10,341,342,343,345,346,349,350,352],{},"Tu app busca en ",[14,344,66],{}," salvo que le digas otra cosa, así que una tabla de\ncualquier otro esquema no existe para ella. Por eso el mensaje dice\n",[14,347,348],{},"public.invoices",": ",[14,351,66],{}," es la planta a la que fue el ascensor.",[10,354,355],{},"Pasa de tres maneras:",[209,357,358,371,380],{},[212,359,360,361,364,365,368,369],{},"tu builder creó la tabla en un esquema propio, como ",[14,362,363],{},"billing"," o ",[14,366,367],{},"private",", y la\napp llama a un simple ",[14,370,240],{},[212,372,373,374,377,378],{},"el esquema va escrito dentro del nombre, como ",[14,375,376],{},".from('billing.invoices')",".\nPostgREST lee la cadena entera como un solo nombre de tabla y la busca en\n",[14,379,66],{},[212,381,382,383,168,386,391,392,394,395,398,399],{},"la tabla es ",[14,384,385],{},"auth.users",[51,387,390],{"href":388,"rel":389},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Fauth\u002Fmanaging-user-data",[55],"guía de Supabase sobre datos de usuario","\ndice: \"For security, the Auth schema is not exposed in the auto-generated API.\"\nSu respuesta es una tabla tuya en ",[14,393,66],{},", ",[14,396,397],{},"profiles"," en su ejemplo, que llena\nun trigger cuando alguien se registra, y\n",[51,400,402],{"href":401},"\u002Fblog\u002Fdatabase-error-saving-new-user","ese trigger puede fallar a su manera",[10,404,405,406,409,410,415,416,418],{},"Una tabla de otra planta se lee con ",[14,407,408],{},".schema('billing').from('invoices')",", y ese\nesquema tiene que figurar en Exposed schemas en los ajustes de la API del\nproyecto, como muestra la\n",[51,411,414],{"href":412,"rel":413},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Fapi\u002Fusing-custom-schemas",[55],"guía de Supabase sobre esquemas propios",".\nHasta entonces, la respuesta pasa a ser ",[14,417,146],{},":",[19,420,423],{"className":421,"code":422,"language":24},[22],"{\n  \"code\": \"PGRST106\",\n  \"details\": null,\n  \"hint\": \"Only the following schemas are exposed: public, graphql_public\",\n  \"message\": \"Invalid schema: billing\"\n}\n",[14,424,422],{"__ignoreMap":27},[10,426,427,428,431,432,437],{},"La pista enumera las plantas en las que el ascensor sí para. PostgREST 12 redacta\nel mismo error de otra forma, con la lista dentro del propio mensaje:\n",[14,429,430],{},"The schema must be one of the following: public, graphql_public",". Es el texto\nque produce ",[51,433,436],{"href":434,"rel":435},"https:\u002F\u002Fgithub.com\u002FPostgREST\u002Fpostgrest\u002Fblob\u002Fv12.2.12\u002Fsrc\u002FPostgREST\u002FError.hs",[55],"su código para esa versión",",\ny el changelog movió el nombre del esquema al mensaje en la 13.0.7.",[10,439,440,441,60],{},"Tu clave secreta sube en el mismo ascensor. La exposición es un ajuste de la API,\nasí que el código de servidor que usa supabase-js con esa clave recibe el mismo\n",[14,442,146],{},[444,445],"diagram",{"alt":446,"caption":447,"src":448},"Un edificio en sección con tres plantas, public abajo, billing en medio y auth arriba, y un hueco de ascensor a la derecha. El ascensor para en public, donde la puerta invoices es un hueco vacío discontinuo que lleva a una placa con PGRST205 y 404. Su parada en billing, donde está la puerta invoices, es un hueco ámbar discontinuo que lleva a una placa con PGRST106 y 406. La planta auth no tiene ninguna parada.","El ascensor solo para en las plantas expuestas. Una tabla de otra planta no está en public, y pedir esa planta por su nombre se rechaza hasta que se expone.","\u002Fblog\u002Fsupabase-relation-does-not-exist\u002Fthe-lift-stops-at-public-1600x760.png",[39,450,452],{"id":451},"qué-abre-exponer-un-esquema","¿Qué abre exponer un esquema?",[10,454,455],{},"El ascensor pasa a parar en esa planta, y con los permisos que da la guía de\nSupabase, cada visitante recibe una llave de cada sala que hay en ella. La Row\nLevel Security es entonces lo único que decide qué puede leer o cambiar cada uno.",[10,457,458,459,463],{},"La ",[51,460,462],{"href":412,"rel":461},[55],"guía sobre esquemas propios","\ntiene dos pasos. El primero es el ajuste Exposed schemas. El segundo es un bloque\nde SQL, y sus dos primeras líneas son:",[19,465,468],{"className":466,"code":467,"language":322,"meta":27},[320],"GRANT USAGE ON SCHEMA myschema TO anon, authenticated, service_role;\nGRANT ALL ON ALL TABLES IN SCHEMA myschema TO anon, authenticated, service_role;\n",[14,469,467],{"__ignoreMap":27},[10,471,472,475,476,479,480,483],{},[14,473,474],{},"anon"," es el rol que hay detrás de cada petición de alguien que no ha iniciado\nsesión. ",[14,477,478],{},"ALL"," es leer, añadir, cambiar y borrar. El bloque sigue con\n",[14,481,482],{},"ALTER DEFAULT PRIVILEGES",", que da el mismo acceso a cada tabla que se cree después\nen esa planta.",[10,485,486,487,490],{},"Eso es seguro donde la Row Level Security, las reglas por fila que Supabase\ncomprueba en cada petición, está activada y escrita para cada tabla. La\n",[51,488,173],{"href":171,"rel":489},[55],"\ndice: \"Tables created through the Supabase Dashboard have RLS enabled by default.\nEnable RLS explicitly for tables created in the SQL Editor or through another\ntool.\" Un esquema que tu builder montó en SQL puede tener tablas con ella\ndesactivada. Expuesta con esos permisos, una tabla así entrega cada fila a\ncualquiera que tenga tu clave pública, y además acepta sus cambios.",[10,492,493],{},"Compruébalo antes de exponer:",[19,495,498],{"className":496,"code":497,"language":322,"meta":27},[320],"select tablename, rowsecurity\nfrom pg_tables\nwhere schemaname = 'billing';\n",[14,499,497],{"__ignoreMap":27},[10,501,502,503,506,507,511,512,516],{},"Cada fila que dice ",[14,504,505],{},"false"," es una tabla sin ninguna regla.\n",[51,508,510],{"href":509},"\u002Fblog\u002Fenable-rls-on-every-supabase-table","Activar Row Level Security en cada tabla","\nexplica cómo activarla y escribir la primera regla, y\n",[51,513,515],{"href":514},"\u002Fblog\u002Fsupabase-rls-disabled-in-public","\"RLS disabled in public\""," explica la regla\nde lectura abierta que deja una tabla igual de accesible con ella activada.",[10,518,519],{},"También puedes dar menos permisos que la guía. Una planta con una tabla que solo\nleen los usuarios con sesión iniciada necesita dos líneas:",[19,521,524],{"className":522,"code":523,"language":322,"meta":27},[320],"grant usage on schema billing to authenticated;\ngrant select on billing.invoices to authenticated;\n",[14,525,523],{"__ignoreMap":27},[10,527,528,529,533],{},"Si tus tablas le responden a un desconocido es algo que se puede comprobar desde\nfuera. Nuestro ",[51,530,532],{"href":531},"\u002Fsecurity-scanner","escaneo gratuito"," lee tu app en producción con\nla misma clave pública que recibe un visitante, te dice qué tablas devuelven filas\ny no necesita cuenta.",[444,535],{"alt":536,"caption":537,"src":538},"La planta billing dibujada dos veces. A la izquierda el ascensor no para allí y tres puertas están cerradas. A la derecha el ascensor para allí y las tres puertas están abiertas: dos salas tienen un candado sobre sus filas, y la tercera no tiene ninguno y sus filas son ámbar.","Exponer una planta con los permisos de la guía abre todas sus puertas. La Row Level Security es el candado de cada sala, y una sala sin candado está abierta a cualquiera con tu clave pública.","\u002Fblog\u002Fsupabase-relation-does-not-exist\u002Fexposing-opens-every-room-1600x700.png",[39,540,542],{"id":541},"exponer-el-esquema-o-mover-la-tabla","¿Exponer el esquema o mover la tabla?",[10,544,545,546,548],{},"Decide según quién lee la tabla. Una tabla que tu app lee desde el navegador\nnecesita un permiso para el rol que la lee y Row Level Security con una regla, esté\ndonde esté. Moverla a ",[14,547,66],{}," te ahorra el paso de exponer el esquema, y nada más.",[10,550,551],{},"Si la mueves:",[19,553,556],{"className":554,"code":555,"language":322,"meta":27},[320],"alter table billing.invoices set schema public;\n",[14,557,555],{"__ignoreMap":27},[10,559,560,561,564],{},"La tabla conserva sus permisos y sus reglas. Una función cuyo cuerpo nombra\n",[14,562,563],{},"billing.invoices"," falla después con este mismo error, porque el cuerpo guarda el\nnombre tal como se escribió.",[10,566,567,568,570,571,575,576,579],{},"Una tabla que solo necesita el código de servidor puede quedarse en una planta a la\nque el ascensor nunca va. Una función en ",[14,569,66],{}," puede hacer con ella la única\ntarea que tu app necesita, y\n",[51,572,574],{"href":573},"\u002Fblog\u002Fsupabase-function-not-found","llamar a una función de Supabase"," explica cómo\nquitar ",[14,577,578],{},"execute"," a los visitantes que no han iniciado sesión.",[39,581,583],{"id":582},"causa-3-una-función-con-la-ruta-de-búsqueda-vacía","Causa 3: una función con la ruta de búsqueda vacía",[10,585,586,587,590,591,593,594,596],{},"Una función con ",[14,588,589],{},"search_path = ''"," no recorre ninguna planta, así que cada tabla\nque usa necesita su esquema delante: ",[14,592,348],{},", nunca un ",[14,595,16],{}," a\nsecas.",[10,598,599,600,608],{},"El Security Advisor de Supabase pide justo ese ajuste. Su\n",[51,601,604,605],{"href":602,"rel":603},"https:\u002F\u002Fgithub.com\u002Fsupabase\u002Fsplinter\u002Fblob\u002Fmain\u002Fdocs\u002F0011_function_search_path_mutable.md",[55],"aviso ",[14,606,607],{},"function_search_path_mutable","\ndice: \"We recommend pinning functions' search_path to an empty string\". La misma\npágina termina con: \"once you set the search_path = '' all references to\ntables\u002Ffunctions\u002Fviews\u002Fetc in your function's body must be qualified with a\nschema name.\"",[10,610,611],{},"Así que el aviso se arregla añadiendo una línea, y la función falla después en el\nprimer nombre de tabla sin esquema:",[19,613,616],{"className":614,"code":615,"language":322,"meta":27},[320],"create or replace function public.count_invoices()\nreturns bigint\nlanguage plpgsql\nset search_path = ''\nas $$\nbegin\n  return (select count(*) from invoices);  -- relation \"invoices\" does not exist\nend;\n$$;\n",[14,617,615],{"__ignoreMap":27},[10,619,620,621,624],{},"Escribe ",[14,622,623],{},"from public.invoices"," y vuelve a funcionar. Mantén el ajuste: está para\nque quien llame a la función no pueda apuntar un nombre sin esquema a una tabla\nsuya.",[10,626,627,628,631,632,634,635,639,640,60],{},"Llamada desde tu app con ",[14,629,630],{},".rpc()",", el error atraviesa PostgREST como ",[14,633,48],{}," con\nHTTP 404, el estado que\n",[51,636,638],{"href":177,"rel":637},[55],"el código de PostgREST","\nda a una tabla desconocida. Un trigger de nuevos registros que falla así aparece\ncomo ",[51,641,642],{"href":401},"\"Database error saving new user\"",[444,644],{"alt":645,"caption":646,"src":647},"Dos búsquedas desde una función marcada con search_path = ''. La de arriba pide invoices sin planta, encuentra una lista de plantas vacía y acaba en una placa con 42P01. La de abajo pide public.invoices, va directa a la puerta invoices de la planta public y lleva una marca de visto.","Con la ruta de búsqueda vacía no hay ninguna planta que recorrer, así que solo una dirección completa encuentra la sala.","\u002Fblog\u002Fsupabase-relation-does-not-exist\u002Fan-empty-search-path-1600x620.png",[39,649,651],{"id":650},"causa-4-la-tabla-todavía-no-está-en-ese-proyecto","Causa 4: la tabla todavía no está en ese proyecto",[10,653,654],{},"La tabla existe en otra base de datos, o solo en tu código.",[10,656,657,658,662,663,665],{},"Una cuenta de Supabase puede tener varios proyectos, y\n",[51,659,661],{"href":660},"\u002Fblog\u002Fsupabase-branching-is-not-a-backup","una rama es un proyecto aparte"," con su\npropia base de datos. Una tabla creada en el SQL Editor del proyecto equivocado es\nuna sala en otro edificio.\n",[51,664,334],{"href":333},"\nmuestra cómo comparar la referencia del proyecto en la dirección de Supabase de tu\napp con la del dashboard.",[10,667,668,669,674],{},"Si construyes con Lovable, repasa el chat buscando SQL que te pidieran aprobar. La\n",[51,670,673],{"href":671,"rel":672},"https:\u002F\u002Fdocs.lovable.dev\u002Fintegrations\u002Fsupabase",[55],"documentación de Supabase de Lovable","\ndice que \"writes the SQL, shows it to you, and asks for your approval in the\nproject chat before running it\". Hasta que lo apruebas, la tabla solo existe en el\ncódigo que la lee.",[39,676,678],{"id":677},"mover-una-tabla-de-la-que-depende-tu-app","Mover una tabla de la que depende tu app",[10,680,681],{},"Guarda una copia de la base de datos de antes de moverla, y entérate cuando cambie\nalgo de lo que tu app expone.",[10,683,684],{},[685,686,687,691],"strong",{},[51,688,690],{"href":689},"\u002Fsupabase-backups","Reeve Care"," guarda una copia de tu base de datos de Supabase:",[209,693,694,697,703,706,709,712],{},[212,695,696],{},"una copia cifrada según un calendario, en el almacenamiento propio de Reeve en la UE, fuera de tu cuenta de Supabase",[212,698,699,700,702],{},"cada esquema que creaste, ",[14,701,66],{}," y los demás, con sus tablas y filas tal como estaban antes de tu cambio",[212,704,705],{},"cada copia verificada antes de contar, contando las filas de cada tabla",[212,707,708],{},"también tus archivos subidos, en cuanto conectas una credencial de Storage",[212,710,711],{},"una restauración con un clic, con una copia de seguridad del estado actual tomada antes",[212,713,714],{},"todo lo que hace Monitor",[10,716,717],{},[685,718,719,723],{},[51,720,722],{"href":721},"\u002Fwebsite-monitoring","Reeve Monitor"," sigue comprobando desde fuera:",[209,725,726,729,732,739],{},[212,727,728],{},"si tu app está en línea, comprobado cada minuto",[212,730,731],{},"la comprobación de seguridad completa cada hora, en hasta tres apps",[212,733,734,735,738],{},"un mensaje cuando un resultado ",[685,736,737],{},"cambia",", para que un despliegue que cambia lo que tu app expone te llegue sin que tengas que ir a mirar",[212,740,741],{},"un informe mensual, en lenguaje claro, de lo que vio",[39,743,745],{"id":744},"qué-hacer-ahora","Qué hacer ahora",[747,748,749],"key-takeaways",{},[209,750,751,758,761,774,783,792],{},[212,752,753,754,757],{},"Lee el mensaje: el nombre entre comillas es lo que se pidió, y ",[14,755,756],{},"public."," delante es el esquema que se recorrió.",[212,759,760],{},"Compara el nombre con el Table Editor, mayúsculas incluidas. Una tabla con una mayúscula en el nombre necesita comillas dobles en SQL.",[212,762,763,764,767,768,770,771,60],{},"Averigua en qué esquema está la tabla con la consulta a ",[14,765,766],{},"information_schema.tables",", y llama a una tabla fuera de ",[14,769,66],{}," con ",[14,772,773],{},".schema('nombre').from(...)",[212,775,776,777,780,781,60],{},"Antes de exponer un esquema, ejecuta la consulta a ",[14,778,779],{},"pg_tables"," y activa la Row Level Security en cada tabla que diga ",[14,782,505],{},[212,784,785,786,788,789,60],{},"Dentro de una función con ",[14,787,589],{},", escribe cada tabla como ",[14,790,791],{},"public.nombre",[212,793,794],{},"Comprueba que el proyecto al que llama tu app es aquel en el que creaste la tabla.",[10,796,797,798,801],{},"Empieza por los dos nombres del mensaje, la tabla y el esquema que va delante,\nporque dicen exactamente qué puerta se pidió y en qué planta. Antes de mover,\nrenombrar o exponer algo que la gente usa,\n",[51,799,800],{"href":689},"guarda una copia de la base de datos"," de antes del cambio.",{"title":27,"searchDepth":803,"depth":803,"links":804},3,[805,807,808,809,810,811,812,813,814],{"id":41,"depth":806,"text":42},2,{"id":194,"depth":806,"text":195},{"id":338,"depth":806,"text":339},{"id":451,"depth":806,"text":452},{"id":541,"depth":806,"text":542},{"id":582,"depth":806,"text":583},{"id":650,"depth":806,"text":651},{"id":677,"depth":806,"text":678},{"id":744,"depth":806,"text":745},"Fundamentos de seguridad","\u002Fblog\u002Fsupabase-relation-does-not-exist\u002Fcover-1200x630.png","Un edificio en sección junto a un hueco de ascensor. El ascensor está en la planta baja, y su parada un piso más arriba es un hueco ámbar discontinuo.","Supabase \"relation does not exist\" o PGRST205: ninguna tabla tiene ese nombre en ese esquema. Cuatro comprobaciones, y lo que abre exponer un esquema.",false,"md",[822,825,828,831,834],{"q":823,"a":824},"¿Por qué Supabase dice que mi tabla no existe?","Porque en el esquema que recorrió la consulta no hay ninguna tabla con ese nombre exacto. Comprueba cuatro cosas en orden: las mayúsculas del nombre, el esquema donde está la tabla (tu app busca en public salvo que le digas otra cosa), el proyecto con el que habla tu app, y si la consulta se ejecuta dentro de una función con el search_path vacío, donde cada tabla necesita su esquema delante.",{"q":826,"a":827},"¿Qué significa \"the schema must be one of the following\"?","Es el error PGRST106 con la redacción de las versiones antiguas de PostgREST. Tu app pidió un esquema que el proyecto no expone, por ejemplo con .schema('nombre'), y la lista que sigue son los esquemas expuestos. Las versiones nuevas dicen \"Invalid schema: nombre\" y ponen la misma lista en la pista. Exponer el esquema lo arregla, y conviene decidirlo a conciencia.",{"q":829,"a":830},"¿Es seguro exponer un esquema en Supabase?","Solo cuando cada tabla que contiene tiene la Row Level Security activada y una regla escrita. La guía de Supabase sobre esquemas propios da a anon, el rol de cualquier visitante que no ha iniciado sesión, acceso completo a cada tabla del esquema, incluidas las que se creen después. Su guía de seguridad añade que las tablas creadas en el SQL Editor no tienen Row Level Security por defecto.",{"q":832,"a":833},"¿Importan las mayúsculas en el nombre de una tabla de Supabase?","Sí. Un nombre escrito en SQL sin comillas dobles se pasa a minúsculas, mientras que el Table Editor y supabase-js conservan las mayúsculas tal como se escribieron. Una tabla creada como Invoices se escribe \"Invoices\", con comillas, en SQL, y .from('Invoices') en tu app.",{"q":835,"a":836},"¿Debería mover la tabla a public?","Moverla arregla la mitad del error que tiene que ver con el esquema y nada de la seguridad: una tabla que tu app lee desde el navegador necesita un permiso para el rol que la lee y Row Level Security con una regla, esté donde esté. Una tabla que solo necesita el código de servidor puede quedarse en un esquema no expuesto, al que se llega con una función en public que hace la única tarea que tu app necesita.","\u002Fblog\u002Fsupabase-relation-does-not-exist\u002Fcard-800x500.png",[839,840,841,842,843,844,845,846,847],"relation does not exist supabase","supabase relation does not exist","could not find the table in the schema cache","pgrst205","42p01","the schema must be one of the following","pgrst106","supabase esquema no expuesto","supabase tabla no encontrada api",{},true,"\u002Fblog\u002Fsupabase-relation-does-not-exist","2026-10-23",{"title":5,"description":818},"blog\u002Fsupabase-relation-does-not-exist",[855,856,857],"Supabase \"relation does not exist\" (código de Postgres 42P01) significa que no hay ninguna tabla ni vista con ese nombre exacto en los esquemas que recorrió la consulta. Desde tu app, en un proyecto actual, el mismo problema se lee \"Could not find the table in the schema cache\" (PGRST205).","Comprueba primero el nombre letra por letra, mayúsculas incluidas, luego el esquema y luego el proyecto. Dentro de una función con el search_path vacío, cada tabla necesita su esquema delante: public.invoices.","Exponer un esquema hace que el error desaparezca y deja que la API llegue a todas las tablas que contiene, así que revisa la Row Level Security de cada una antes de hacerlo.","Nm445BO1wAGfk73vqEyv9Peevkk9njJrvzCvE5c6aNg",[860,866,872,878,884,890,896,902,908,914,920,927,933,939,945,951,957,963,964,969,974,980,986,992,998,1004,1010,1015,1021,1027,1033,1039,1045,1051,1057,1063,1069,1075,1081,1087,1093,1099,1105,1111,1117,1123,1127,1133,1139,1145,1151,1157,1163,1169,1175,1181,1187,1193,1199,1205,1211,1217,1222,1227,1233,1239,1245,1251,1257,1263,1269,1275,1281,1287,1293,1299,1305,1311,1316,1322,1328,1334,1340,1346,1351,1357,1363,1369,1374,1380],{"path":861,"title":862,"description":863,"published":864,"category":815,"image":865,"draft":819},"\u002Fblog\u002Fmax-client-connections-reached","Supabase \"max client connections reached\", explicado","Supabase \"max client connections reached\": el pooler no tiene sitio para un cliente más. El puerto de tu cadena de conexión decide el límite.","2026-11-09","\u002Fblog\u002Fmax-client-connections-reached\u002Fcard-800x500.png",{"path":867,"title":868,"description":869,"published":870,"category":815,"image":871,"draft":819},"\u002Fblog\u002Funsupported-provider-provider-is-not-enabled","Error de Supabase: \"Unsupported provider: provider is not enabled\"","\"Unsupported provider: provider is not enabled\": Google está desactivado en tu proyecto Supabase. Cómo activarlo y arreglar redirect_uri_mismatch.","2026-11-08","\u002Fblog\u002Funsupported-provider-provider-is-not-enabled\u002Fcard-800x500.png",{"path":873,"title":874,"description":875,"published":876,"category":815,"image":877,"draft":819},"\u002Fblog\u002Fviolates-foreign-key-constraint","Error de Supabase \"violates foreign key constraint\" al insertar","Supabase \"insert or update on table violates foreign key constraint\": tu fila apunta a una fila que no existe. Cuál, por qué, y cómo arreglarlo.","2026-11-07","\u002Fblog\u002Fviolates-foreign-key-constraint\u002Fcard-800x500.png",{"path":879,"title":880,"description":881,"published":882,"category":815,"image":883,"draft":819},"\u002Fblog\u002Fnull-value-violates-not-null-constraint","Error de Supabase \"null value in column violates not-null constraint\"","Supabase \"null value in column violates not-null constraint\": una columna obligatoria llegó vacía. De dónde vino el null, y cómo arreglarlo.","2026-11-06","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fcard-800x500.png",{"path":885,"title":886,"description":887,"published":888,"category":815,"image":889,"draft":819},"\u002Fblog\u002Fauth-session-missing","Supabase \"Auth session missing!\": adónde fue la sesión","\"Auth session missing!\" es supabase-js sin encontrar ninguna sesión iniciada y sin llegar a preguntar a Supabase. Adónde fue la sesión, y el arreglo.","2026-11-05","\u002Fblog\u002Fauth-session-missing\u002Fcard-800x500.png",{"path":891,"title":892,"description":893,"published":894,"category":815,"image":895,"draft":819},"\u002Fblog\u002Finvalid-claim-missing-sub-claim","Supabase \"invalid claim: missing sub claim\": no hay nadie conectado","\"invalid claim: missing sub claim\": Supabase Auth recibió un token auténtico sin ningún usuario, la clave del proyecto en lugar de una sesión.","2026-11-04","\u002Fblog\u002Finvalid-claim-missing-sub-claim\u002Fcard-800x500.png",{"path":897,"title":898,"description":899,"published":900,"category":815,"image":901,"draft":819},"\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function","\"Failed to send a request to the Edge Function\": la causa real","\"Failed to send a request to the Edge Function\": el navegador no recibió una respuesta legible. Su consola dice por qué y si tu función se ejecutó.","2026-11-03","\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function\u002Fcard-800x500.png",{"path":903,"title":904,"description":905,"published":906,"category":815,"image":907,"draft":819},"\u002Fblog\u002Fsupabase-auth-redirects-to-localhost","Por qué los enlaces de email de Supabase redirigen a localhost:3000","Si Supabase redirige a localhost:3000, la Site URL nunca se cambió o tu dirección falta en Redirect URLs. La solución, y el comodín que debes evitar.","2026-11-02","\u002Fblog\u002Fsupabase-auth-redirects-to-localhost\u002Fcard-800x500.png",{"path":909,"title":910,"description":911,"published":912,"category":815,"image":913,"draft":819},"\u002Fblog\u002Femail-link-is-invalid-or-has-expired","Supabase \"Email link is invalid or has expired\" con un enlace nuevo","Con un enlace enviado hace un minuto, \"Email link is invalid or has expired\" en Supabase suele indicar que un filtro de correo lo usó antes. Cómo arreglarlo.","2026-11-01","\u002Fblog\u002Femail-link-is-invalid-or-has-expired\u002Fcard-800x500.png",{"path":915,"title":916,"description":917,"published":918,"category":815,"image":919,"draft":819},"\u002Fblog\u002Fcanceling-statement-due-to-statement-timeout","Supabase \"canceling statement due to statement timeout\", explicado","Supabase \"canceling statement due to statement timeout\": una consulta superó el límite de su rol, 3 segundos para un visitante. Revisa antes la política RLS.","2026-10-31","\u002Fblog\u002Fcanceling-statement-due-to-statement-timeout\u002Fcard-800x500.png",{"path":921,"title":922,"description":923,"published":924,"category":925,"image":926,"draft":819},"\u002Fblog\u002Fdatabase-error-deleting-user","\"Database error deleting user\" en Supabase, y qué se va con él","\"Database error deleting user\" en Supabase: tu base de datos rechazó el borrado. La causa está en tus logs y el arreglo decide qué más se borra.","2026-10-30","Backups","\u002Fblog\u002Fdatabase-error-deleting-user\u002Fcard-800x500.png",{"path":928,"title":929,"description":930,"published":931,"category":815,"image":932,"draft":819},"\u002Fblog\u002Fstripe-webhook-no-signatures-found","\"No signatures found matching the expected signature\" en Supabase","¿Tu webhook de Stripe en una Edge Function de Supabase dice \"No signatures found matching the expected signature\"? Tiene dos causas. Sigue verificando.","2026-10-29","\u002Fblog\u002Fstripe-webhook-no-signatures-found\u002Fcard-800x500.png",{"path":934,"title":935,"description":936,"published":937,"category":815,"image":938,"draft":819},"\u002Fblog\u002Fsupabase-invalid-login","\"Invalid login credentials\" en Supabase: las tres causas","Supabase dice \"Invalid login credentials\" ante una contraseña errónea, un email desconocido y una cuenta de Google. Cómo saber cuál es.","2026-10-28","\u002Fblog\u002Fsupabase-invalid-login\u002Fcard-800x500.png",{"path":940,"title":941,"description":942,"published":943,"category":815,"image":944,"draft":819},"\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code","\"Edge Function returned a non-2xx status code\": el error real","\"Edge Function returned a non-2xx status code\" significa que tu función de Supabase respondió con un error. Aquí ves dónde leer el real.","2026-10-27","\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code\u002Fcard-800x500.png",{"path":946,"title":947,"description":948,"published":949,"category":815,"image":950,"draft":819},"\u002Fblog\u002Fcertificate-not-trusted","Certificado no confiable: tres causas aparte de la caducidad","¿Certificado no confiable aunque la fecha está bien? Nombra otra dirección, se firmó a sí mismo o a su cadena le falta un eslabón. Cómo saber cuál.","2026-10-26","\u002Fblog\u002Fcertificate-not-trusted\u002Fcard-800x500.png",{"path":952,"title":953,"description":954,"published":955,"category":815,"image":956,"draft":819},"\u002Fblog\u002Finvalid-input-syntax-for-type-uuid","Supabase \"invalid input syntax for type uuid\": lee las comillas","Supabase \"invalid input syntax for type uuid\" significa que tu app envió algo que no es un id. Lee las comillas, frena la consulta, conserva la columna.","2026-10-25","\u002Fblog\u002Finvalid-input-syntax-for-type-uuid\u002Fcard-800x500.png",{"path":958,"title":959,"description":960,"published":961,"category":815,"image":962,"draft":819},"\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint","Supabase \"duplicate key value violates unique constraint\", explicado","Supabase \"duplicate key value violates unique constraint\": el valor ya está ocupado. Encuentra la columna, elige un arreglo y aparta el valor de la pantalla.","2026-10-24","\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint\u002Fcard-800x500.png",{"path":850,"title":5,"description":818,"published":851,"category":815,"image":837,"draft":819},{"path":573,"title":965,"description":966,"published":967,"category":815,"image":968,"draft":819},"Supabase PGRST202: \"Could not find the function in the schema cache\"","Supabase \"Could not find the function in the schema cache\" muestra los nombres de argumentos que envió tu app. Compáralos con la función.","2026-10-22","\u002Fblog\u002Fsupabase-function-not-found\u002Fcard-800x500.png",{"path":333,"title":970,"description":971,"published":972,"category":815,"image":973,"draft":819},"Supabase PGRST204: \"Could not find the column in the schema cache\"","Supabase \"Could not find the column in the schema cache\" suele ser un nombre que no coincide. Cuatro comprobaciones y la línea que recarga.","2026-10-21","\u002Fblog\u002Fsupabase-schema-cache-column-error\u002Fcard-800x500.png",{"path":975,"title":976,"description":977,"published":978,"category":815,"image":979,"draft":819},"\u002Fblog\u002Fsupabase-email-rate-limit-exceeded","Supabase \"Email Rate Limit Exceeded\": dos límites, un solo código","Supabase \"Email Rate Limit Exceeded\" limita cada email que envía tu proyecto: 2 por hora con el remitente integrado. Qué cuenta y cómo subirlo.","2026-10-20","\u002Fblog\u002Fsupabase-email-rate-limit-exceeded\u002Fcard-800x500.png",{"path":981,"title":982,"description":983,"published":984,"category":815,"image":985,"draft":819},"\u002Fblog\u002Fsupabase-users-logged-out","Supabase \"Invalid Refresh Token: Refresh Token Not Found\": 3 causas","Supabase \"Invalid Refresh Token: Refresh Token Not Found\" cierra la sesión de tus usuarios. Tres causas, y cómo las separa el log de Auth.","2026-10-19","\u002Fblog\u002Fsupabase-users-logged-out\u002Fcard-800x500.png",{"path":987,"title":988,"description":989,"published":990,"category":815,"image":991,"draft":819},"\u002Fblog\u002Fpermission-denied-for-schema-public","Supabase \"permission denied for schema public\": tres causas","Supabase \"permission denied for schema public\" es un grant que falta en el esquema. Qué lo quitó y la línea que lo devuelve.","2026-10-18","\u002Fblog\u002Fpermission-denied-for-schema-public\u002Fcard-800x500.png",{"path":993,"title":994,"description":995,"published":996,"category":815,"image":997,"draft":819},"\u002Fblog\u002Fsupabase-bucket-not-found","Supabase \"Bucket not found\": las tres cosas que significa","Supabase \"Bucket not found\" es una de tres cosas: un enlace público a un bucket privado, un nombre que no coincide exactamente o el proyecto equivocado.","2026-10-17","\u002Fblog\u002Fsupabase-bucket-not-found\u002Fcard-800x500.png",{"path":999,"title":1000,"description":1001,"published":1002,"category":815,"image":1003,"draft":819},"\u002Fblog\u002Fsupabase-invalid-jwt","Supabase invalid JWT: ¿qué clave dejó de funcionar?","Un error Supabase invalid JWT es PGRST301: un token firmado no pasó la verificación. En un proyecto nuevo se trata de la sesión, no de la clave de API.","2026-10-16","\u002Fblog\u002Fsupabase-invalid-jwt\u002Fcard-800x500.png",{"path":1005,"title":1006,"description":1007,"published":1008,"category":815,"image":1009,"draft":819},"\u002Fblog\u002Fsupabase-single-row-error","Supabase \"multiple (or no) rows returned\" casi siempre es ninguna","Supabase devuelve PGRST116, \"JSON object requested, multiple (or no) rows returned\", cuando tu consulta no encontró nada. Una fila oculta parece ausente.","2026-10-15","\u002Fblog\u002Fsupabase-single-row-error\u002Fcard-800x500.png",{"path":401,"title":1011,"description":1012,"published":1013,"category":815,"image":1014,"draft":819},"\"Database error saving new user\" en Supabase: dónde mirar","\"Database error saving new user\" significa que algo unido a tu registro de Supabase falló y se deshizo entero. El error real está en tus logs.","2026-10-14","\u002Fblog\u002Fdatabase-error-saving-new-user\u002Fcard-800x500.png",{"path":1016,"title":1017,"description":1018,"published":1019,"category":815,"image":1020,"draft":819},"\u002Fblog\u002Fwhat-is-supabase","¿Qué es Supabase? La base de datos que eligió tu builder","Qué es Supabase: la base de datos, el login y el almacenamiento de archivos de tu app. Supabase lleva los servidores; las reglas sobre tus filas son tuyas.","2026-10-13","\u002Fblog\u002Fwhat-is-supabase\u002Fcard-800x500.png",{"path":1022,"title":1023,"description":1024,"published":1025,"category":815,"image":1026,"draft":819},"\u002Fblog\u002Faws-access-key-exposed-in-frontend","Una clave AWS expuesta en tu frontend no es como una clave OpenAI","Una clave de acceso de AWS expuesta en tu frontend lee y escribe tu almacenamiento. La solución suele ser una presigned URL, no una clave nueva.","2026-10-12","\u002Fblog\u002Faws-access-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":1028,"title":1029,"description":1030,"published":1031,"category":815,"image":1032,"draft":819},"\u002Fblog\u002Fstorage-upload-violates-row-level-security","\"Row-level security policy for table objects\" al subir","\"New row violates row-level security policy for table objects\" significa que tu subida no tiene regla insert. Hacer público el bucket no añade ninguna.","2026-10-11","\u002Fblog\u002Fstorage-upload-violates-row-level-security\u002Fcard-800x500.png",{"path":1034,"title":1035,"description":1036,"published":1037,"category":815,"image":1038,"draft":819},"\u002Fblog\u002Fbase44-security-scan","Escaneo de seguridad de Base44: lo que solo él ve","El escaneo de seguridad de Base44 revisa siete tipos de problema desde dentro. Esta es la mitad que lee y nadie más puede leer, y la mitad que nunca mira.","2026-10-10","\u002Fblog\u002Fbase44-security-scan\u002Fcard-800x500.png",{"path":1040,"title":1041,"description":1042,"published":1043,"category":815,"image":1044,"draft":819},"\u002Fblog\u002Fapi-key-leaked-what-to-do","Tu clave API se filtró. El orden en el que hay que actuar","Una clave API se filtró y no sabes por dónde empezar. No todas las claves de tu frontend lo son, y el orden importa más que la velocidad.","2026-10-09","\u002Fblog\u002Fapi-key-leaked-what-to-do\u002Fcard-800x500.png",{"path":1046,"title":1047,"description":1048,"published":1049,"category":815,"image":1050,"draft":819},"\u002Fblog\u002Finfinite-recursion-in-policy-for-relation","\"Infinite recursion detected in policy\" sin desactivar RLS","\"Infinite recursion detected in policy for relation\" significa que tu policy preguntó a la tabla que protege. Así se rompe el círculo.","2026-10-08","\u002Fblog\u002Finfinite-recursion-in-policy-for-relation\u002Fcard-800x500.png",{"path":1052,"title":1053,"description":1054,"published":1055,"category":815,"image":1056,"draft":819},"\u002Fblog\u002Fno-api-key-found-in-request","\"No API key found in request\" en Supabase, y el arreglo equivocado","\"No API key found in request\" significa que tu petición llegó a Supabase sin clave. Casi todas las respuestas apuntan a las reglas de tu base de datos.","2026-10-07","\u002Fblog\u002Fno-api-key-found-in-request\u002Fcard-800x500.png",{"path":1058,"title":1059,"description":1060,"published":1061,"category":815,"image":1062,"draft":819},"\u002Fblog\u002Fis-base44-safe","¿Es seguro Base44? Lo que vimos en 5.442 apps Base44","¿Es seguro Base44? Pasamos nueve comprobaciones a 5.442 apps Base44 en producción. Casi todas sacaron B por motivos que su dueño nunca eligió.","2026-10-06","\u002Fblog\u002Fis-base44-safe\u002Fcard-800x500.png",{"path":1064,"title":1065,"description":1066,"published":1067,"category":815,"image":1068,"draft":819},"\u002Fblog\u002Fmove-a-secret-to-an-edge-function","Ocultar una clave API: muévela a una Edge Function de Supabase","Ocultar una clave API significa sacarla del navegador, y una Edge Function de Supabase es el sitio más pequeño donde ponerla. Dos pasos importan más.","2026-10-05","\u002Fblog\u002Fmove-a-secret-to-an-edge-function\u002Fcard-800x500.png",{"path":1070,"title":1071,"description":1072,"published":1073,"category":815,"image":1074,"draft":819},"\u002Fblog\u002Fenv-file-exposed-on-your-server","¿Tu archivo .env está expuesto? Las doce rutas a revisar","¿Tu archivo .env está expuesto en tu propio servidor web? Doce direcciones te lo dicen, y un acierto significa que todo lo que contiene ya es público.","2026-10-04","\u002Fblog\u002Fenv-file-exposed-on-your-server\u002Fcard-800x500.png",{"path":1076,"title":1077,"description":1078,"published":1079,"category":815,"image":1080,"draft":819},"\u002Fblog\u002Fis-v0-safe","Seguridad de v0: las 1.790 aplicaciones escaneadas sacaron una A","Seguridad de v0, medida en 1.790 aplicaciones v0 en vivo: todas sacaron A. Solo 17 nombraban una base de datos, y eso es casi todo lo que mide la A.","2026-10-03","\u002Fblog\u002Fis-v0-safe\u002Fcard-800x500.png",{"path":1082,"title":1083,"description":1084,"published":1085,"category":815,"image":1086,"draft":819},"\u002Fblog\u002Fis-bolt-safe","¿Es seguro Bolt? Lo que vimos en 1.123 aplicaciones Bolt","¿Es seguro Bolt? Pasamos 1.123 aplicaciones Bolt en vivo por nueve comprobaciones. El hosting salió limpio. Los hallazgos eran claves y tablas abiertas dentro.","2026-10-02","\u002Fblog\u002Fis-bolt-safe\u002Fcard-800x500.png",{"path":1088,"title":1089,"description":1090,"published":1091,"category":815,"image":1092,"draft":819},"\u002Fblog\u002Fis-supabase-down-or-your-app","¿Supabase está caído o es tu app? Cómo distinguirlo","¿Supabase está caído o es tu app? Un proyecto pausado, una base de datos llena y un fallo tuyo parecen lo mismo. Cuatro comprobaciones los separan.","2026-10-01","\u002Fblog\u002Fis-supabase-down-or-your-app\u002Fcard-800x500.png",{"path":1094,"title":1095,"description":1096,"published":1097,"category":815,"image":1098,"draft":819},"\u002Fblog\u002Fdoes-supabase-encrypt-my-data","¿Supabase cifra mis datos? Sí. Esto es lo que detiene","¿Supabase cifra los datos? Sí: AES-256 en reposo, TLS en tránsito, SOC 2 e ISO 27001. Qué cubre cada uno, y la fuga que ninguno de ellos detiene.","2026-09-30","\u002Fblog\u002Fdoes-supabase-encrypt-my-data\u002Fcard-800x500.png",{"path":1100,"title":1101,"description":1102,"published":1103,"category":815,"image":1104,"draft":819},"\u002Fblog\u002Fsupabase-free-plan-limits","¿Cuánto cuesta Supabase? Los límites del plan gratuito","Los límites del plan gratuito de Supabase: 500 MB de base de datos, 5 GB de egress y ningún backup. Qué pasa en cada uno y qué cambia Pro por $25.","2026-09-29","\u002Fblog\u002Fsupabase-free-plan-limits\u002Fcard-800x500.png",{"path":1106,"title":1107,"description":1108,"published":1109,"category":925,"image":1110,"draft":819},"\u002Fblog\u002Ftest-your-supabase-backup","Prueba tu backup de Supabase antes del día que lo necesites","Cómo probar tu backup de Supabase: restaurarlo en un proyecto de prueba, contar filas, iniciar sesión y buscar la línea que le falta a un archivo cortado.","2026-09-28","\u002Fblog\u002Ftest-your-supabase-backup\u002Fcard-800x500.png",{"path":1112,"title":1113,"description":1114,"published":1115,"category":925,"image":1116,"draft":819},"\u002Fblog\u002Fsupabase-backup-github-action","Backup gratis de Supabase con una GitHub Action, y la trampa","Un backup de Supabase con GitHub Action es gratis y sirve a muchas apps. El workflow, el connection string correcto y el egress de cada ejecución.","2026-09-27","\u002Fblog\u002Fsupabase-backup-github-action\u002Fcard-800x500.png",{"path":1118,"title":1119,"description":1120,"published":1121,"category":925,"image":1122,"draft":819},"\u002Fblog\u002Fdownload-your-supabase-backup","Por qué no puedes descargar tu backup de Supabase","No puedes descargar tu backup de Supabase en un proyecto actual: la copia diaria es una instantánea física. Cómo comprobarlo y tener tu propia copia.","2026-09-26","\u002Fblog\u002Fdownload-your-supabase-backup\u002Fcard-800x500.png",{"path":190,"title":1124,"description":1125,"published":1121,"category":815,"image":1126,"draft":819},"Supabase \"permission denied for table\": el grant que falta","Desde el 30 de octubre, una tabla nueva de Supabase responde \"permission denied for table\" hasta que le das acceso. El grant del email es la mitad del arreglo.","\u002Fblog\u002Fsupabase-permission-denied-for-table\u002Fcard-800x500.png",{"path":1128,"title":1129,"description":1130,"published":1131,"category":815,"image":1132,"draft":819},"\u002Fblog\u002Fopen-api-endpoint-exposed","¿Un endpoint de API abierto es un problema? Mira el JSON","Tu escaneo señaló un endpoint de API abierto. Que importe depende de lo que devolvió, y la mayoría de los que encontramos eran de la plataforma.","2026-09-25","\u002Fblog\u002Fopen-api-endpoint-exposed\u002Fcard-800x500.png",{"path":1134,"title":1135,"description":1136,"published":1137,"category":815,"image":1138,"draft":819},"\u002Fblog\u002Flovable-security-scan","Escaneo de seguridad de Lovable: lo que no puede probar","Escaneo de seguridad de Lovable: qué revisan el Quick scan y el Deep scan, cuándo corre cada uno, y lo único que ningún escaneo desde dentro puede probar.","2026-09-24","\u002Fblog\u002Flovable-security-scan\u002Fcard-800x500.png",{"path":1140,"title":1141,"description":1142,"published":1143,"category":925,"image":1144,"draft":819},"\u002Fblog\u002Fsupabase-backup-auth-users","Por qué tu dump de Supabase no tiene usuarios dentro","Ejecuta supabase db dump por sí solo y obtienes la forma de tu base de datos y ninguna de sus filas, sin el esquema auth donde viven tus usuarios.","2026-09-23","\u002Fblog\u002Fsupabase-backup-auth-users\u002Fcard-800x500.png",{"path":1146,"title":1147,"description":1148,"published":1149,"category":815,"image":1150,"draft":819},"\u002Fblog\u002Fdomain-and-certificate-expiry","Dominio caducado, web caída: qué pasa a partir de ahora","Tu dominio caducó y tu web está caída. Aquí tienes el reloj que acaba de arrancar, por qué el certificado es el menor problema, y cómo comprobar ambos.","2026-09-22","\u002Fblog\u002Fdomain-and-certificate-expiry\u002Fcard-800x500.png",{"path":1152,"title":1153,"description":1154,"published":1155,"category":815,"image":1156,"draft":819},"\u002Fblog\u002Fis-lovable-safe","¿Es seguro Lovable? Lo que vimos en 18.554 aplicaciones Lovable","¿Es seguro Lovable? Pasamos 18.554 aplicaciones Lovable por nueve comprobaciones. La plataforma fue la más limpia de cinco. Los hallazgos estaban dentro.","2026-09-21","\u002Fblog\u002Fis-lovable-safe\u002Fcard-800x500.png",{"path":1158,"title":1159,"description":1160,"published":1161,"category":815,"image":1162,"draft":819},"\u002Fblog\u002Fvibe-coded-app-security-checklist","La lista de seguridad del vibe coding, en nueve comprobaciones","Una lista de seguridad del vibe coding con nueve puntos, cada uno verificable en tu app desde fuera por cualquiera, y cada uno con una prueba de una línea.","2026-09-20","\u002Fblog\u002Fvibe-coded-app-security-checklist\u002Fcard-800x500.png",{"path":1164,"title":1165,"description":1166,"published":1167,"category":815,"image":1168,"draft":819},"\u002Fblog\u002Fstripe-secret-key-in-frontend","Una clave secreta de Stripe en tu frontend puede mover dinero","Una clave secreta de Stripe expuesta en tu frontend puede reembolsar, cobrar y leer cada ficha de cliente que tienes. Tu clave pk_live_ sí debe estar ahí.","2026-09-19","\u002Fblog\u002Fstripe-secret-key-in-frontend\u002Fcard-800x500.png",{"path":1170,"title":1171,"description":1172,"published":1173,"category":815,"image":1174,"draft":819},"\u002Fblog\u002Fvite-and-next-public-env-vars","Variables de entorno de Vite expuestas: el prefijo dice publica esto","Variables de entorno de Vite expuestas en tu app: hicieron lo que pedía el prefijo. VITE_ y NEXT_PUBLIC_ significan publica esto, y la IA nunca supo el coste.","2026-09-18","\u002Fblog\u002Fvite-and-next-public-env-vars\u002Fcard-800x500.png",{"path":1176,"title":1177,"description":1178,"published":1179,"category":815,"image":1180,"draft":819},"\u002Fblog\u002Fbase44-source-maps","Seguridad en Base44: qué marca un escaneo y qué te toca arreglar a ti","Seguridad en Base44 en 5.438 apps escaneadas: tres hallazgos en casi todas son de la plataforma, el source map es la insignia de Base44 y lo tuyo es poco.","2026-09-17","\u002Fblog\u002Fbase44-source-maps\u002Fcard-800x500.png",{"path":1182,"title":1183,"description":1184,"published":1185,"category":815,"image":1186,"draft":819},"\u002Fblog\u002Fis-cursor-ai-safe","¿Es seguro Cursor AI? El editor, el código y la app que publicaste","¿Es seguro Cursor AI? Tres preguntas en una búsqueda: qué guarda Cursor, en qué falla el código que escribe y si la app que publicaste está abierta.","2026-09-16","\u002Fblog\u002Fis-cursor-ai-safe\u002Fcard-800x500.png",{"path":1188,"title":1189,"description":1190,"published":1191,"category":815,"image":1192,"draft":819},"\u002Fblog\u002Fis-replit-safe","¿Es seguro Replit? Lo que vimos en 3.042 aplicaciones Replit en vivo","¿Es seguro Replit? Pasamos 3.042 aplicaciones Replit en vivo por nueve comprobaciones externas. Los hallazgos estaban en la aplicación publicada, no en el host.","2026-09-15","\u002Fblog\u002Fis-replit-safe\u002Fcard-800x500.png",{"path":1194,"title":1195,"description":1196,"published":1197,"category":925,"image":1198,"draft":819},"\u002Fblog\u002Fsupabase-storage-backup","Supabase storage backup: tu copia de la base no tiene archivos","Un Supabase storage backup es una tarea aparte. Las copias de la base guardan la lista de tus archivos y ninguno de ellos, así que cada upload queda roto.","2026-09-14","\u002Fblog\u002Fsupabase-storage-backup\u002Fcard-800x500.png",{"path":1200,"title":1201,"description":1202,"published":1203,"category":925,"image":1204,"draft":819},"\u002Fblog\u002Fsupabase-point-in-time-recovery","Point-in-time recovery de Supabase: precio y límites","El point-in-time recovery de Supabase rebobina tu base de datos a cualquier segundo de la última semana. Cuesta $100 al mes sobre Pro y solo cubre la base.","2026-09-13","\u002Fblog\u002Fsupabase-point-in-time-recovery\u002Fcard-800x500.png",{"path":1206,"title":1207,"description":1208,"published":1209,"category":925,"image":1210,"draft":819},"\u002Fblog\u002Fsupabase-project-paused-recover","¿Proyecto de Supabase pausado? Tus datos siguen ahí","Supabase pausó tu proyecto tras una semana sin actividad. No se ha borrado nada, Restaurar está junto al nombre y tienes un año antes de que eso cambie.","2026-09-12","\u002Fblog\u002Fsupabase-project-paused-recover\u002Fcard-800x500.png",{"path":1212,"title":1213,"description":1214,"published":1215,"category":815,"image":1216,"draft":819},"\u002Fblog\u002Fsafest-ai-app-builder","¿Qué builder de apps con IA es más seguro? 30.998 apps escaneadas","¿Qué builder de apps con IA es más seguro? Escaneamos 30.998 apps activas de Lovable, Base44, Replit, v0 y Bolt. El builder no es lo que decide tu nota.","2026-09-11","\u002Fblog\u002Fsafest-ai-app-builder\u002Fcard-800x500.png",{"path":509,"title":1218,"description":1219,"published":1220,"category":815,"image":1221,"draft":819},"Activa Row Level Security en cada tabla de Supabase y compruébalo","Activar Row Level Security en Supabase sin política cierra la tabla del todo. Una política sin el interruptor no hace nada. Aquí tienes el SQL y la prueba.","2026-09-10","\u002Fblog\u002Fenable-rls-on-every-supabase-table\u002Fcard-800x500.png",{"path":514,"title":1223,"description":1224,"published":1225,"category":815,"image":1226,"draft":819},"Supabase \"RLS disabled in public\": lo que el aviso no ve","Supabase marca \"RLS disabled in public\" como error. No dice nada de la política de lectura que deja tu tabla igual de abierta a cualquiera.","2026-09-09","\u002Fblog\u002Fsupabase-rls-disabled-in-public\u002Fcard-800x500.png",{"path":1228,"title":1229,"description":1230,"published":1231,"category":815,"image":1232,"draft":819},"\u002Fblog\u002Frotate-supabase-service-role-key","Cómo rotar una clave service_role de Supabase filtrada","Supabase dice que primero cierres la fuga. Otras guías dicen que rotes ya. Cuál es lo correcto depende de dónde se filtró tu clave service_role.","2026-09-08","\u002Fblog\u002Frotate-supabase-service-role-key\u002Fcard-800x500.png",{"path":1234,"title":1235,"description":1236,"published":1237,"category":815,"image":1238,"draft":819},"\u002Fblog\u002Fvibe-coding-security-scanners-compared","Escáneres de seguridad vibe coding comparados, el nuestro incluido","El mejor escáner de seguridad para vibe coding depende de tres preguntas que ninguna lista de funciones responde. Ocho herramientas, con precios.","2026-09-07","\u002Fblog\u002Fvibe-coding-security-scanners-compared\u002Fcard-800x500.png",{"path":1240,"title":1241,"description":1242,"published":1243,"category":815,"image":1244,"draft":819},"\u002Fblog\u002Fsupabase-security-checker","Supabase security checker: haz tú mismo las cinco comprobaciones","Un Supabase security checker lee tu app publicada en lugar de la configuración de tu proyecto. Estas son las cinco comprobaciones y cómo hacer cada una.","2026-09-06","\u002Fblog\u002Fsupabase-security-checker\u002Fcard-800x500.png",{"path":1246,"title":1247,"description":1248,"published":1249,"category":815,"image":1250,"draft":819},"\u002Fblog\u002Fvibe-coding-security-scanner","Escáner de seguridad para vibe coding: qué se le escapa","Un escáner de seguridad para vibe coding lee tu app en vivo desde fuera. Qué cubre eso, las cuatro cosas que no puede ver y cómo leer el resultado.","2026-09-05","\u002Fblog\u002Fvibe-coding-security-scanner\u002Fcard-800x500.png",{"path":1252,"title":1253,"description":1254,"published":1255,"category":815,"image":1256,"draft":819},"\u002Fblog\u002Fmissing-security-headers","Faltan cabeceras de seguridad: cuándo importa de verdad","Que falten cabeceras de seguridad es el hallazgo que más imprime nuestro escáner. Contra qué protegen y cuándo son la línea menos urgente de tu informe.","2026-09-04","\u002Fblog\u002Fmissing-security-headers\u002Fcard-800x500.png",{"path":1258,"title":1259,"description":1260,"published":1261,"category":815,"image":1262,"draft":819},"\u002Fblog\u002Fopenai-api-key-exposed-in-frontend","Tu clave de API de OpenAI está expuesta en tu frontend. Rótala.","Una clave de API de OpenAI expuesta en tu frontend no se puede atar a un dominio. Rótala hoy, mueve la llamada a tu servidor y limita el gasto.","2026-09-03","\u002Fblog\u002Fopenai-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":1264,"title":1265,"description":1266,"published":1267,"category":815,"image":1268,"draft":819},"\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps","Clave de API expuesta en el frontend: qué llevan 30.998 apps","Una clave de API expuesta en tu frontend suele ser una clave de Google Maps. Escaneamos 30.998 apps vibe-coded y contamos qué secretos se filtran de verdad.","2026-09-02","\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps\u002Fcard-800x500.png",{"path":1270,"title":1271,"description":1272,"published":1273,"category":925,"image":1274,"draft":819},"\u002Fblog\u002Fsupabase-backup-tools-compared","Herramientas de backup de Supabase comparadas, la nuestra incluida","Cuatro tipos de herramienta de backup de Supabase, qué copia cada una y cuándo una GitHub Action gratuita le gana a pagar a cualquiera, nosotros incluidos.","2026-09-01","\u002Fblog\u002Fsupabase-backup-tools-compared\u002Fcard-800x500.png",{"path":1276,"title":1277,"description":1278,"published":1279,"category":815,"image":1280,"draft":819},"\u002Fblog\u002Freplit-secrets-explained","Cómo usar secrets en Replit, y qué se publica de todos modos","Cómo usar secrets en Replit: añadir uno, leerlo y arreglar las dos causas del undefined. Y las claves que la herramienta Secrets no puede mantener privadas.","2026-08-31","\u002Fblog\u002Freplit-secrets-explained\u002Fcard-800x500.png",{"path":1282,"title":1283,"description":1284,"published":1285,"category":925,"image":1286,"draft":819},"\u002Fblog\u002Fback-up-supabase-free-tier","Backups del plan gratuito de Supabase: cómo hacer uno sin terminal","No hay backups en el plan gratuito de Supabase: la copia tienes que hacerla tú. Cómo sacarla desde el panel y qué deja fuera la vía CSV.","2026-08-30","\u002Fblog\u002Fback-up-supabase-free-tier\u002Fcard-800x500.png",{"path":1288,"title":1289,"description":1290,"published":1291,"category":815,"image":1292,"draft":819},"\u002Fblog\u002Fis-supabase-secure","¿Supabase es seguro? Sí. Tu proyecto es otra pregunta","¿Supabase es seguro? La plataforma está auditada, cifrada y sometida a pentests. Su propia documentación dice dónde termina eso y empiezan tus ajustes.","2026-08-29","\u002Fblog\u002Fis-supabase-secure\u002Fcard-800x500.png",{"path":1294,"title":1295,"description":1296,"published":1297,"category":815,"image":1298,"draft":819},"\u002Fblog\u002Fwhere-to-find-supabase-api-keys","Dónde están tus claves API de Supabase: anon, service_role y URL","La URL del proyecto, la clave anon y la clave service_role están en una sola página del panel. Aquí tienes dónde está, y cuál de los cuatro va en tu app.","2026-08-28","\u002Fblog\u002Fwhere-to-find-supabase-api-keys\u002Fcard-800x500.png",{"path":1300,"title":1301,"description":1302,"published":1303,"category":815,"image":1304,"draft":819},"\u002Fblog\u002Fnew-row-violates-row-level-security-policy","New row violates row-level security policy en Supabase. ¿Y ahora?","\"New row violates row-level security policy\" significa que Supabase rechazó una escritura. El arreglo rápido vuelve a abrir la tabla para cualquiera.","2026-08-27","\u002Fblog\u002Fnew-row-violates-row-level-security-policy\u002Fcard-800x500.png",{"path":1306,"title":1307,"description":1308,"published":1309,"category":815,"image":1310,"draft":819},"\u002Fblog\u002Fcors-wildcard-security-risk","¿Es un comodín CORS un riesgo de seguridad? Casi nunca.","¿Es un comodín CORS un riesgo de seguridad? Casi siempre es el ajuste por defecto de tu builder y no entrega nada que tu servidor no diera ya a cualquiera.","2026-08-26","\u002Fblog\u002Fcors-wildcard-security-risk\u002Fcard-800x500.png",{"path":660,"title":1312,"description":1313,"published":1314,"category":925,"image":1315,"draft":819},"Supabase branching no es un backup. Solo va hacia adelante.","Por qué el branching de Supabase no es un backup: una rama arranca sin tus datos y un merge solo mueve el esquema. Para qué sirve y qué usar en su lugar.","2026-08-25","\u002Fblog\u002Fsupabase-branching-is-not-a-backup\u002Fcard-800x500.png",{"path":1317,"title":1318,"description":1319,"published":1320,"category":925,"image":1321,"draft":819},"\u002Fblog\u002Fhow-to-restore-a-supabase-backup","Cómo restaurar un backup de Supabase, y qué se rompe después","Cómo restaurar un backup de Supabase desde el panel o desde un archivo de volcado, qué reemplaza la restauración y por qué tu app puede seguir rota al terminar.","2026-08-24","\u002Fblog\u002Fhow-to-restore-a-supabase-backup\u002Fcard-800x500.png",{"path":1323,"title":1324,"description":1325,"published":1326,"category":815,"image":1327,"draft":819},"\u002Fblog\u002Fsupabase-storage-bucket-public","Tu bucket de Supabase Storage es público. ¿Es un problema?","Un bucket público de Supabase Storage significa que quien tenga la URL de un archivo puede abrirlo. No significa que alguien pueda listar lo que hay dentro.","2026-08-23","\u002Fblog\u002Fsupabase-storage-bucket-public\u002Fcard-800x500.png",{"path":1329,"title":1330,"description":1331,"published":1332,"category":815,"image":1333,"draft":819},"\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend","¿Es un problema una clave de API de Google en tu frontend?","Una clave de API de Google en tu frontend es la clave que más encuentra nuestro escáner, y casi siempre no pasa nada. Un ajuste gratuito decide cuál es cuál.","2026-08-22","\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":1335,"title":1336,"description":1337,"published":1338,"category":925,"image":1339,"draft":819},"\u002Fblog\u002Fai-agent-deleted-my-database","Un agente de IA borró mis datos de Supabase. ¿Qué puedo recuperar?","Un agente de IA borró datos de tu base. Lo que puedes recuperar ya estaba decidido antes de que se ejecutara, y los próximos minutos deciden el resto.","2026-08-21","\u002Fblog\u002Fai-agent-deleted-my-database\u002Fcard-800x500.png",{"path":1341,"title":1342,"description":1343,"published":1344,"category":815,"image":1345,"draft":819},"\u002Fblog\u002Fcan-anyone-read-your-supabase-database","¿Puede cualquiera leer tu base de datos Supabase? 3.680 apps","¿Puede cualquiera leer tu base de datos Supabase sin iniciar sesión? Escaneamos 30.998 apps activas hechas con builders de IA y medimos cuántas dicen que sí.","2026-08-18","\u002Fblog\u002Fcan-anyone-read-your-supabase-database\u002Fcard-800x500.png",{"path":1347,"title":1348,"description":1349,"published":1344,"category":815,"image":1350,"draft":819},"\u002Fblog\u002Fsource-maps-exposed-in-production","Source maps expuestos: tu app está publicando su código original","Un source map expuesto deja que cualquiera lea el código original de tu app, comentarios incluidos. La comprobación de 30 segundos y lo que de verdad importa.","\u002Fblog\u002Fsource-maps-exposed-in-production\u002Fcard-800x500.png",{"path":1352,"title":1353,"description":1354,"published":1355,"category":815,"image":1356,"draft":819},"\u002Fblog\u002Fsupabase-new-api-keys","Supabase publishable key: ¿es la misma que la anon key?","Supabase sustituyó anon y service_role por claves publicables y secretas. ¿Cuál pertenece a tu app y cuál no debe estar nunca?","2026-08-12","\u002Fblog\u002Fsupabase-new-api-keys\u002Fcard-800x500.png",{"path":1358,"title":1359,"description":1360,"published":1361,"category":925,"image":1362,"draft":819},"\u002Fblog\u002Fdoes-supabase-back-up-my-database","¿Supabase respalda mi base de datos? Depende de tu plan.","¿Supabase respalda tu base de datos? A diario en los planes de pago, y nada en el gratuito. Cómo saber cuál tienes y qué es lo que esa copia no cubre.","2026-08-11","\u002Fblog\u002Fdoes-supabase-back-up-my-database\u002Fcard-800x500.png",{"path":1364,"title":1365,"description":1366,"published":1367,"category":815,"image":1368,"draft":819},"\u002Fblog\u002Fsupabase-rls-on-but-table-still-public","Supabase Row Level Security está activado. Tu tabla sigue pública.","Activar Supabase Row Level Security no protege una tabla. Lo hacen tus políticas, y la que arregló tu app rota puede dejar entrar a cualquiera.","2026-08-10","\u002Fblog\u002Fsupabase-rls-on-but-table-still-public\u002Fcard-800x500.png",{"path":1370,"title":1371,"description":1372,"published":1367,"category":925,"image":1373,"draft":819},"\u002Fblog\u002Fversion-history-is-not-a-backup","El historial de versiones no es un backup. No recupera una tabla.","Lovable y Bolt guardan un historial de versiones de tu código. Tu base de datos es un servicio aparte, y volver atrás no te devuelve tus datos.","\u002Fblog\u002Fversion-history-is-not-a-backup\u002Fcard-800x500.png",{"path":1375,"title":1376,"description":1377,"published":1378,"category":925,"image":1379,"draft":819},"\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database","Tres formas de respaldar una base Supabase, y qué falta en cada una","El panel, pg_dump y un servicio gestionado. Qué guarda de verdad cada uno, qué deja fuera sin avisar y cuál sobrevive a perder la cuenta.","2026-08-09","\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database\u002Fcard-800x500.png",{"path":1381,"title":1382,"description":1383,"published":1378,"category":815,"image":1384,"draft":819},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","Qué claves de API son seguras en el navegador y cuáles no","Tu clave anon de Supabase está pensada para ser pública. Tu clave service_role no, y se salta todas las reglas que pongas. Cómo distinguirlas.","\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend\u002Fcard-800x500.png",1794213645177]