[{"data":1,"prerenderedAt":1236},["ShallowReactive",2],{"blog-es-supabase-schema-cache-column-error":3,"blog-index-es":708},{"id":4,"title":5,"body":6,"category":666,"cover":667,"coverAlt":668,"description":669,"draft":670,"extension":671,"faq":672,"image":687,"keywords":688,"meta":697,"navigation":698,"ogTitle":5,"path":699,"published":700,"seo":701,"stem":702,"tldr":703,"updated":700,"__hash__":707},"blog_es\u002Fblog\u002Fsupabase-schema-cache-column-error.md","Supabase PGRST204: \"Could not find the column in the schema cache\"",{"type":7,"value":8,"toc":652},"minimark",[9,13,24,31,39,44,47,50,73,114,117,123,127,140,149,158,226,238,241,249,256,264,273,277,280,283,289,312,315,339,359,364,368,376,385,401,405,426,439,444,448,451,457,472,475,481,484,488,491,499,502,514,518,521,529,535,549,553,556,563,583,592,610,614,645],[10,11,12],"p",{},"Le pediste a tu builder que añadiera un número de teléfono al formulario de\nperfil. La columna está en el Table Editor. Pulsas Guardar, no pasa nada, y la\nconsola del navegador muestra esto:",[14,15,20],"pre",{"className":16,"code":18,"language":19},[17],"language-text","{\n  \"code\": \"PGRST204\",\n  \"details\": null,\n  \"hint\": null,\n  \"message\": \"Could not find the 'phoneNumber' column of 'profiles' in the schema cache\"\n}\n","text",[21,22,18],"code",{"__ignoreMap":23},"",[10,25,26,27,30],{},"Supabase \"Could not find the column in the schema cache\" significa que tu app\nintentó guardar un campo que la tabla, tal como la ve PostgREST, no tiene.\nPostgREST es la parte de Supabase que convierte las llamadas\n",[21,28,29],{},"supabase.from(...)"," de tu app en consultas a la base de datos. Guarda su propia\nlista de cada tabla y cada columna, llamada schema cache, para no tener que\npreguntarle a la base de datos por su forma en cada petición.",[10,32,33,34,38],{},"Esta es la parte que casi todas las respuestas cuentan mal: ",[35,36,37],"strong",{},"te dicen que\nrecargues la schema cache."," En Supabase la caché se recarga sola cada vez que\ncambia una tabla. Así que si acabas de añadir una columna y sigues viendo este\nerror, la caché es la razón menos probable. Lo habitual es que sea el nombre.",[40,41,43],"h2",{"id":42},"qué-significa-could-not-find-the-column-in-the-schema-cache","¿Qué significa \"Could not find the column in the schema cache\"?",[10,45,46],{},"Una clave de una escritura que envió tu app no tiene columna correspondiente en\nesa tabla.",[10,48,49],{},"Piensa en la schema cache como el directorio del vestíbulo de un edificio: una\nlínea para cada oficina, que es una tabla, y debajo cada sala, que es una\ncolumna. Cada petición de tu app es una hoja de entrega. PostgREST lee cada\nlínea de la hoja contra el directorio, y una sala que el directorio no tiene se\nrechaza en recepción, antes de que nada llegue a tus datos.",[10,51,52,53,56,57,60,61,64,65,72],{},"El mensaje nombra los dos lados de esa comparación. En ",[21,54,55],{},"Could not find the 'phoneNumber' column of 'profiles' in the schema cache",", ",[21,58,59],{},"phoneNumber"," es lo que\nenvió tu app y ",[21,62,63],{},"profiles"," la tabla a la que iba. Otras tres cosas están fijadas\nen\n",[66,67,71],"a",{"href":68,"rel":69},"https:\u002F\u002Fgithub.com\u002FPostgREST\u002Fpostgrest\u002Fblob\u002Fmain\u002Fsrc\u002Flibrary\u002FPostgREST\u002FError.hs",[70],"nofollow","el propio código de PostgREST",":",[74,75,76,84,98],"ul",{},[77,78,79,80,83],"li",{},"el código es ",[21,81,82],{},"PGRST204"," y el estado HTTP 400, que significa que la petición\nmisma estaba mal",[77,85,86,87,56,90,93,94,97],{},"solo sale de escrituras: ",[21,88,89],{},".insert()",[21,91,92],{},".update()"," y ",[21,95,96],{},".upsert()",". Una lectura\nque nombra una columna inexistente falla con otro error, escrito por Postgres",[77,99,100,101,105,106,109,110,113],{},"no trae ninguna pista. Para\n",[66,102,104],{"href":103},"\u002Fblog\u002Fsupabase-relation-does-not-exist","una tabla que falta",", PostgREST\nsugiere el nombre más parecido que conoce; para una columna que falta, ",[21,107,108],{},"hint"," es ",[21,111,112],{},"null"," y la\ncomparación te toca a ti",[10,115,116],{},"Cada clave del objeto que pasas a una de esas tres llamadas tiene que ser una\ncolumna de esa tabla, escrita como la escribe la tabla.",[118,119],"diagram",{"alt":120,"caption":121,"src":122},"Una hoja de entrega con tres líneas junto al directorio profiles, que lista cuatro salas. Dos líneas de la hoja llevan a salas con el mismo nombre y están marcadas. La tercera, phoneNumber, termina en ámbar en un signo de distinto junto a phone_number.","PostgREST compara cada clave que envía tu app con las columnas de su lista, letra por letra. phoneNumber y phone_number son dos nombres distintos.","\u002Fblog\u002Fsupabase-schema-cache-column-error\u002Fletter-for-letter-1600x700.png",[40,124,126],{"id":125},"causa-1-el-nombre-no-coincide-letra-por-letra","Causa 1: el nombre no coincide, letra por letra",[10,128,129,130,56,132,135,136,139],{},"La comprobación es exacta. Para PostgREST, ",[21,131,59],{},[21,133,134],{},"phone_number"," y\n",[21,137,138],{},"phonenumber"," son tres columnas distintas, y tu tabla tiene como mucho una.",[10,141,142,143,145,146,148],{},"Hay dos formas habituales de llegar aquí. La primera son dos costumbres de\nnombres que se cruzan: JavaScript suele escribir un campo como ",[21,144,59],{},", y\nuna columna de Postgres suele llamarse ",[21,147,134],{},". El código que escribió tu\nbuilder y el SQL que ejecutó pueden estar bien cada uno por su lado y aun así no\ncoincidir.",[10,150,151,152,157],{},"La segunda es la trampa de las comillas. En SQL, un nombre escrito sin comillas\ndobles se pasa a minúsculas. La\n",[66,153,156],{"href":154,"rel":155},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fsql-syntax-lexical.html",[70],"documentación de Postgres","\nlo dice claramente: \"Quoting an identifier also makes it case-sensitive, whereas\nunquoted names are always folded to lower case.\"",[159,160,161,177],"table",{},[162,163,164],"thead",{},[165,166,167,171,174],"tr",{},[168,169,170],"th",{},"Lo que ejecutaste en el SQL Editor",[168,172,173],{},"La columna que creó",[168,175,176],{},"Lo que tu app tiene que enviar",[178,179,180,196,211],"tbody",{},[165,181,182,188,192],{},[183,184,185],"td",{},[21,186,187],{},"alter table profiles add column phone_number text;",[183,189,190],{},[21,191,134],{},[183,193,194],{},[21,195,134],{},[165,197,198,203,207],{},[183,199,200],{},[21,201,202],{},"alter table profiles add column phoneNumber text;",[183,204,205],{},[21,206,138],{},[183,208,209],{},[21,210,138],{},[165,212,213,218,222],{},[183,214,215],{},[21,216,217],{},"alter table profiles add column \"phoneNumber\" text;",[183,219,220],{},[21,221,59],{},[183,223,224],{},[21,225,59],{},[10,227,228,229,231,232,234,235,237],{},"La fila del medio es la que atrapa a la gente. El SQL parece haber creado\n",[21,230,59],{},", el código envía ",[21,233,59],{},", y la columna es ",[21,236,138],{},".",[10,239,240],{},"Para ver los nombres reales, abre la tabla en el Table Editor y lee los\nencabezados de las columnas, o ejecuta esto en el SQL Editor:",[14,242,247],{"className":243,"code":245,"language":246,"meta":23},[244],"language-sql","select column_name\nfrom information_schema.columns\nwhere table_schema = 'public' and table_name = 'profiles';\n","sql",[21,248,245],{"__ignoreMap":23},[10,250,251,252,255],{},"Después mira qué envió tu app. En las herramientas de desarrollo del navegador,\nabre la pestaña Red (Network), haz clic en la petición fallida a\n",[21,253,254],{},"\u002Frest\u002Fv1\u002Fprofiles"," y lee su payload.",[10,257,258,259,109,261,263],{},"La solución más segura es cambiar la app, porque no toca nada de tus datos.\nPídele a tu builder que use el nombre exacto de la columna en todos los sitios\ndonde se guarda ese campo: \"la columna de ",[21,260,63],{},[21,262,134],{},"; usa ese\nnombre en cada insert y cada update\". Renombrar la columna en su lugar es un\ncambio en una tabla en uso, y cualquier otra pantalla que siga usando el nombre\nantiguo se rompe la primera vez que se ejecuta.",[10,265,266,267,272],{},"Si construyes con Lovable, repasa el chat buscando un cambio de base de datos que\nte pidió aprobar. La\n",[66,268,271],{"href":269,"rel":270},"https:\u002F\u002Fdocs.lovable.dev\u002Fintegrations\u002Fsupabase",[70],"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 apruebes, la columna no existe,\nespere lo que espere el código.",[40,274,276],{"id":275},"causa-2-una-clave-que-nunca-fue-una-columna","Causa 2: una clave que nunca fue una columna",[10,278,279],{},"La petición lleva algo que la tabla nunca debía guardar: filas relacionadas\nanidadas dentro del insert, o un campo que el formulario usa para sí mismo.",[10,281,282],{},"El insert anidado es el que la gente espera que funcione:",[14,284,287],{"className":285,"code":286,"language":19},[17],"await supabase.from('orders').insert({\n  customer_id: user.id,\n  total: 42,\n  order_items: [{ product_id: 7, quantity: 2 }],\n})\n",[21,288,286],{"__ignoreMap":23},[10,290,291,292,295,296,299,300,305,306,311],{},"Eso falla con ",[21,293,294],{},"Could not find the 'order_items' column of 'orders' in the schema cache",". Leer la misma forma funciona: ",[21,297,298],{},".select('*, order_items(*)')"," devuelve un\npedido con sus artículos, así que parece que lo contrario también debería.\nPostgREST lee cada clave de una escritura como una columna de la única tabla a\nla que va la petición. Un usuario de Supabase\n",[66,301,304],{"href":302,"rel":303},"https:\u002F\u002Fgithub.com\u002Fsupabase\u002Fsupabase\u002Fissues\u002F50816",[70],"lo reportó como un bug",", y\nla respuesta, antes de que el issue se cerrara el 18 de octubre de 2026, fue que\nlos inserts anidados son una función que falta. La petición lleva abierta en\n",[66,307,310],{"href":308,"rel":309},"https:\u002F\u002Fgithub.com\u002FPostgREST\u002Fpostgrest\u002Fissues\u002F818",[70],"el tracker de PostgREST","\ndesde febrero de 2017.",[10,313,314],{},"Otras dos formas del mismo problema:",[74,316,317,330],{},[77,318,319,320,322,323,326,327,329],{},"una fila leída junto con sus filas relacionadas, editada en un formulario y\ndevuelta entera en ",[21,321,92],{},", de modo que el ",[21,324,325],{},"order_items"," o el ",[21,328,63],{},"\nadjunto va con ella",[77,331,332,333,335,336],{},"el estado completo de un formulario pasado tal cual a ",[21,334,89],{},", con campos\nque necesita el formulario y no la tabla, como ",[21,337,338],{},"confirmPassword",[10,340,341,342,345,346,349,350,353,354,358],{},"La solución es escribir cada tabla por separado, primero el padre. Inserta el\npedido y pide su id de vuelta con ",[21,343,344],{},".select('id').single()",", y después inserta\nlos artículos con ese ",[21,347,348],{},"order_id",". Son dos peticiones, así que si falla la\nsegunda el pedido queda guardado sin sus artículos. Cuando eso no debe pasar\nnunca, pon los dos inserts en una función de Postgres y llámala con ",[21,351,352],{},".rpc()",",\nque la respuesta en ese issue llama el patrón estándar. Así se guarda todo o\nnada. Si esa llamada responde con PGRST202,\n",[66,355,357],{"href":356},"\u002Fblog\u002Fsupabase-function-not-found","la versión de este error para funciones","\nexplica por qué.",[118,360],{"alt":361,"caption":362,"src":363},"Carril superior: una hoja con una segunda hoja anidada dentro va al directorio orders. Sus dos líneas simples están marcadas, y la anidada se detiene en una cruz ámbar unida a una placa con PGRST204 y 400. Carril inferior: dos hojas enviadas una tras otra, numeradas 1 y 2, cada una a su propio directorio, ambas marcadas.","Una hoja anidada se lee como una columna más de la primera tabla. Enviadas por separado, primero el padre, cada una encuentra su propio directorio.","\u002Fblog\u002Fsupabase-schema-cache-column-error\u002Fa-key-that-is-not-a-column-1600x720.png",[40,365,367],{"id":366},"causa-3-el-cambio-fue-a-otro-proyecto","Causa 3: el cambio fue a otro proyecto",[10,369,370,371,375],{},"La columna existe, en una base de datos con la que tu app nunca habla. Una\ncuenta de Supabase puede tener varios proyectos, y una\n",[66,372,374],{"href":373},"\u002Fblog\u002Fsupabase-branching-is-not-a-backup","rama es un proyecto aparte"," con su\npropia base de datos. Una columna añadida en el SQL Editor del equivocado cambia\nel directorio de otro edificio.",[10,377,378,379,384],{},"En el\n",[66,380,383],{"href":381,"rel":382},"https:\u002F\u002Fgithub.com\u002Fsupabase\u002Fsupabase\u002Fissues\u002F42183",[70],"issue #42183"," de Supabase,\nun reporte de que la caché \"does not update\" se intentara lo que se intentara,\nuno de los desarrolladores de PostgREST respondió que el reporte mencionaba dos\nreferencias de proyecto distintas, y pidió confirmar que el cambio se había\nhecho en el proyecto al que iban las peticiones.",[10,386,387,388,391,392,395,396,400],{},"La comprobación usa dos direcciones. Tu app llama a\n",[21,389,390],{},"https:\u002F\u002F\u003Creference>.supabase.co",", que puedes ver en cualquier petición de la\npestaña Red. El panel muestra el proyecto en el que estás en\n",[21,393,394],{},"supabase.com\u002Fdashboard\u002Fproject\u002F\u003Creference>",". Las dos referencias tienen que ser\nla misma cadena.\n",[66,397,399],{"href":398},"\u002Fblog\u002Fwhere-to-find-supabase-api-keys","Cuál es mi proyecto"," explica cómo\nencontrarlo cuando otra persona configuró Supabase.",[40,402,404],{"id":403},"causa-4-la-caché-de-verdad-está-desactualizada","Causa 4: la caché de verdad está desactualizada",[10,406,407,408,411,412,415,416,419,420,425],{},"Es el caso raro en Supabase, porque Supabase recarga la caché por ti. Cada base\nde datos de Supabase se configura con un disparador de eventos,\n",[21,409,410],{},"pgrst_ddl_watch",", que se ejecuta después de cada cambio en la forma de la base\nde datos, ",[21,413,414],{},"ALTER TABLE"," incluido, y envía ",[21,417,418],{},"NOTIFY pgrst, 'reload schema'",": el\nmensaje que le dice a PostgREST que reconstruya su lista. Puedes leerlo en\n",[66,421,424],{"href":422,"rel":423},"https:\u002F\u002Fgithub.com\u002Fsupabase\u002Fpostgres\u002Fblob\u002Fdevelop\u002Fmigrations\u002Fschema-17.sql",[70],"la configuración de base de datos de Supabase",".\nAñadir una columna en el Table Editor o en el SQL Editor vuelve a rotular el\ndirectorio por sí solo.",[10,427,428,429,434,435,438],{},"Sale mal cuando ese mensaje no llega nunca. La\n",[66,430,433],{"href":431,"rel":432},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Ftroubleshooting\u002Fpostgrest-not-recognizing-new-columns-or-functions-bd75f5",[70],"guía de solución de problemas","\nde Supabase describe el caso: \"an underlying Postgres notification queue issue\nprevents PostgREST from receiving cache reload signals after schema changes\".\nEl registro de PostgREST muestra entonces ",[21,436,437],{},"Failed listening for database notifications on the \"pgrst\" channel",", y el directorio se queda como estaba por\nmuchas columnas que añadas.",[118,440],{"alt":441,"caption":442,"src":443},"Carril superior: ALTER TABLE añade una columna a una tabla, suena una campana, una línea marcada NOTIFY pgrst llega al directorio profiles y el directorio recibe la misma placa nueva. Carril inferior: el mismo cambio, la línea cortada por una cruz ámbar y el directorio con un hueco vacío punteado donde debería estar la placa nueva.","Cada ALTER TABLE en Supabase envía el mensaje de recarga por sí solo. Una caché desactualizada significa que ese mensaje no llegó.","\u002Fblog\u002Fsupabase-schema-cache-column-error\u002Fthe-board-repaints-itself-1600x660.png",[40,445,447],{"id":446},"cómo-recargo-la-schema-cache-de-supabase","¿Cómo recargo la schema cache de Supabase?",[10,449,450],{},"Ejecuta una línea en el SQL Editor:",[14,452,455],{"className":453,"code":454,"language":246,"meta":23},[244],"NOTIFY pgrst, 'reload schema';\n",[21,456,454],{"__ignoreMap":23},[10,458,459,460,465,466,471],{},"La\n",[66,461,464],{"href":462,"rel":463},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Ftroubleshooting\u002Frefresh-postgrest-schema",[70],"página de Supabase sobre esto","\ndice que deberías ver \"Success. No rows returned\" bajo Results. Es seguro en una\napp en uso. La\n",[66,467,470],{"href":468,"rel":469},"https:\u002F\u002Fdocs.postgrest.org\u002Fen\u002Fv14\u002Freferences\u002Fschema_cache.html",[70],"documentación de PostgREST","\ndice \"Requests will wait until the schema cache reload is done\", así que nadie\nque esté usando la app ve un error mientras tanto.",[10,473,474],{},"Si el problema es la cola de notificaciones, el mensaje de recarga se atasca\nigual que el automático. La guía de solución de problemas da una segunda línea\npara ese caso:",[14,476,479],{"className":477,"code":478,"language":246,"meta":23},[244],"select pg_notification_queue_usage();\n",[21,480,478],{"__ignoreMap":23},[10,482,483],{},"Según Supabase, hace que PostgREST actualice su caché y \"does not require a\ndatabase instance or PostgREST service restart\". Después vuelve a intentar\nguardar.",[40,485,487],{"id":486},"cuando-recargar-no-sirve","Cuando recargar no sirve",[10,489,490],{},"Reinicia el proyecto, y si el error sobrevive a eso, vuelve a las causas 1 a 3.",[10,492,493,494,498],{},"Un reinicio siempre le da a PostgREST una lista nueva. El mismo desarrollador de\nPostgREST escribió en #42183 que un PostgREST reiniciado \"won't go live if it\ndoesn't load a new schema cache\". Restart project está en Project Settings,\nluego General, y\n",[66,495,497],{"href":496},"\u002Fblog\u002Fis-supabase-down-or-your-app","cómo reiniciar un proyecto de Supabase"," te\nguía, incluidos los pocos minutos que pasa sin conexión.",[10,500,501],{},"Así que un error que sobrevive a un reinicio viene de una lista nueva y exacta:\nesa tabla, en ese proyecto, no tiene una columna con ese nombre. El propio\n#42183, abierto en enero de 2026 por alguien que había probado una recarga, un\nreinicio y borrar y volver a añadir la columna, seguía abierto cuando lo leímos\nel 21 de octubre de 2026. La respuesta del desarrollador allí también sugería\nactualizar a PostgREST 14 desde los ajustes de Infrastructure del proyecto.",[10,503,504,505,508,509,513],{},"Tacha borrar y volver a añadir de tu lista. ",[21,506,507],{},"alter table … drop column"," borra\ncada valor guardado en esa columna, y la nueva columna con el mismo nombre empieza\nvacía. Si estás a punto de cambiar una tabla en la que escriben usuarios reales,\nguarda una copia de la base de datos de antes del cambio.\n",[66,510,512],{"href":511},"\u002Fsupabase-backups","Reeve Care"," hace una según un calendario y la guarda cifrada,\nen el almacenamiento propio de Reeve en la UE.",[40,515,517],{"id":516},"mantener-tus-tipos-generados-al-día","Mantener tus tipos generados al día",[10,519,520],{},"Vuelve a generar tus tipos de TypeScript después de cada cambio en una tabla, y\nun nombre de columna mal escrito puede aparecer en el editor antes de llegar al\nsitio en vivo.",[10,522,523,524,72],{},"Supabase construye el archivo a partir de tus columnas reales, desde el panel o\ncon un comando de su\n",[66,525,528],{"href":526,"rel":527},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Fapi\u002Frest\u002Fgenerating-types",[70],"guía para generar tipos",[14,530,533],{"className":531,"code":532,"language":19},[17],"npx supabase gen types typescript --project-id \"$PROJECT_REF\" --schema public > database.types.ts\n",[21,534,532],{"__ignoreMap":23},[10,536,537,538,541,542,545,546,548],{},"Cuando el cliente se crea con esos tipos, ",[21,539,540],{},"createClient\u003CDatabase>(…)",", el tipo\n",[21,543,544],{},"Insert"," de cada tabla lista las columnas que acepta ",[21,547,89],{},". Una columna mal\nescrita en un objeto escrito completo dentro de la llamada se marca entonces como\nerror antes de publicar. La documentación de Lovable dice que \"regenerates the\nTypeScript types your app uses\" después de un cambio en la base de datos. Un\narchivo de tipos generado antes de tu último cambio en la tabla todavía lista las\ncolumnas antiguas.",[40,550,552],{"id":551},"cambiar-una-tabla-en-uso-sin-perder-lo-que-contiene","Cambiar una tabla en uso sin perder lo que contiene",[10,554,555],{},"Guarda una copia de antes del cambio y sabe al momento si la app sigue\nrespondiendo después.",[10,557,558],{},[35,559,560,562],{},[66,561,512],{"href":511}," guarda una copia de tu base de datos de Supabase:",[74,564,565,568,571,574,577,580],{},[77,566,567],{},"una copia cifrada según un calendario, en el almacenamiento propio de Reeve en la UE, fuera de tu cuenta de Supabase",[77,569,570],{},"la última copia antes de tu cambio tiene las tablas y columnas como estaban, filas incluidas",[77,572,573],{},"cada copia verificada antes de contar, contando las filas de cada tabla",[77,575,576],{},"también tus archivos subidos, en cuanto conectas una credencial de Storage",[77,578,579],{},"restauración con un clic, con una copia de seguridad del estado actual tomada primero",[77,581,582],{},"todo lo que hace Monitor",[10,584,585],{},[35,586,587,591],{},[66,588,590],{"href":589},"\u002Fwebsite-monitoring","Reeve Monitor"," sigue comprobando desde fuera:",[74,593,594,597,600,607],{},[77,595,596],{},"si tu app está en línea, comprobado cada minuto, para distinguir fácilmente un botón Guardar roto de una caída",[77,598,599],{},"la comprobación de seguridad completa cada hora, en hasta tres apps",[77,601,602,603,606],{},"un mensaje cuando un resultado ",[35,604,605],{},"cambia",", para que un deploy que cambia lo que tu app expone te llegue sin que tengas que ir a mirar",[77,608,609],{},"un informe mensual, en lenguaje claro, de lo que vio",[40,611,613],{"id":612},"qué-hacer-ahora","Qué hacer ahora",[615,616,617],"key-takeaways",{},[74,618,619,622,625,628,631,642],{},[77,620,621],{},"Lee los dos nombres del mensaje: la clave que envió tu app y la tabla a la que fue.",[77,623,624],{},"Compara esa clave con la columna en el Table Editor, letra por letra. Un nombre escrito en SQL sin comillas se guarda en minúsculas.",[77,626,627],{},"Busca claves que no son columnas: filas relacionadas anidadas en un insert, o una fila entera devuelta con sus filas relacionadas. Escribe cada tabla por separado, o pon las dos escrituras en una función.",[77,629,630],{},"Comprueba que la referencia en la dirección de Supabase de tu app coincide con el proyecto que cambiaste.",[77,632,633,634,637,638,641],{},"Si todo eso coincide, ejecuta ",[21,635,636],{},"NOTIFY pgrst, 'reload schema';",", después ",[21,639,640],{},"select pg_notification_queue_usage();",", y reinicia el proyecto solo si ninguno de los dos ayuda.",[77,643,644],{},"Vuelve a generar tus tipos de TypeScript después de cada cambio en una tabla.",[10,646,647,648,651],{},"Empieza por el nombre de columna que cita el mensaje, porque es exactamente lo\nque envió tu app, y el Table Editor muestra exactamente lo que tiene la tabla.\nAntes de renombrar o borrar algo en una tabla que la gente usa,\n",[66,649,650],{"href":511},"guarda una copia de la base de datos"," de antes del cambio.",{"title":23,"searchDepth":653,"depth":653,"links":654},3,[655,657,658,659,660,661,662,663,664,665],{"id":42,"depth":656,"text":43},2,{"id":125,"depth":656,"text":126},{"id":275,"depth":656,"text":276},{"id":366,"depth":656,"text":367},{"id":403,"depth":656,"text":404},{"id":446,"depth":656,"text":447},{"id":486,"depth":656,"text":487},{"id":516,"depth":656,"text":517},{"id":551,"depth":656,"text":552},{"id":612,"depth":656,"text":613},"Fundamentos de seguridad","\u002Fblog\u002Fsupabase-schema-cache-column-error\u002Fcover-1200x630.png","Un directorio de vestíbulo con un hueco vacío en su lista de salas y, delante, una hoja de entrega con una línea resaltada en ámbar.","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.",false,"md",[673,676,679,681,684],{"q":674,"a":675},"¿Qué significa PGRST204?","PGRST204 es el código que PostgREST, la parte de Supabase que responde a las llamadas de tu app a la base de datos, le da a una escritura que nombra una columna que la tabla no tiene. Llega con HTTP 400 y el mensaje \"Could not find the 'x' column of 'y' in the schema cache\", donde x es la clave que envió tu app e y la tabla a la que la envió. Lo producen los insert, update y upsert.",{"q":677,"a":678},"Añadí la columna y sigue fallando. ¿Por qué?","Lo habitual es el nombre. Comprueba que tu app envía exactamente el nombre guardado (una columna añadida en SQL como phoneNumber, sin comillas, queda guardada como phonenumber), que la añadiste en el proyecto al que llama tu app y que nada más en la petición es una clave que la tabla no tiene. Supabase recarga la schema cache por sí solo después de cada ALTER TABLE, así que una caché desactualizada es lo último de lo que sospechar.",{"q":447,"a":680},"Ejecuta NOTIFY pgrst, 'reload schema'; en el SQL Editor. Si eso no cambia nada, la guía de solución de problemas de Supabase da un segundo comando, select pg_notification_queue_usage();, para cuando los mensajes de recarga se quedan atascados. Las peticiones que llegan durante la recarga esperan a que termine.",{"q":682,"a":683},"¿Pausar el proyecto la vacía?","Sí. PostgREST construye una schema cache nueva cada vez que arranca, y reanudar un proyecto pausado lo vuelve a arrancar. Restart project, en Project Settings y luego General, hace lo mismo sin la pausa. Si el error sobrevive a cualquiera de los dos, la caché tenía razón desde el principio: a esa tabla, en ese proyecto, le falta la columna.",{"q":685,"a":686},"¿Por qué los inserts anidados fallan con este error?","Porque PostgREST lee cada clave de un insert como una columna de una sola tabla, y una clave que contiene filas relacionadas es una columna que no encuentra. Leer filas relacionadas en una petición funciona; escribirlas así nunca ha sido posible. Inserta primero el padre y luego los hijos con su id, o haz las dos cosas dentro de una función de Postgres llamada con rpc().","\u002Fblog\u002Fsupabase-schema-cache-column-error\u002Fcard-800x500.png",[689,690,691,692,693,694,695,696],"could not find the column in the schema cache","pgrst204","supabase could not find the column of in the schema cache","supabase schema cache no se actualiza","notify pgrst reload schema","supabase columna nueva no funciona","recargar schema cache supabase","supabase insert anidado",{},true,"\u002Fblog\u002Fsupabase-schema-cache-column-error","2026-10-21",{"title":5,"description":669},"blog\u002Fsupabase-schema-cache-column-error",[704,705,706],"Supabase \"Could not find the column in the schema cache\" (PGRST204) significa que tu app intentó guardar un campo que la tabla no tiene en su lista. PostgREST compara cada clave de un insert o un update con su propia copia de tu esquema, letra por letra.","La causa habitual es el nombre: phoneNumber enviado a una columna llamada phone_number, o una clave que nunca fue una columna, como filas relacionadas anidadas dentro de un insert.","Supabase recarga esa copia por sí solo cada vez que cambia una tabla. Cuando de verdad está desactualizada, NOTIFY pgrst, 'reload schema'; en el SQL Editor la recarga mientras la app sigue funcionando.","2lDBT-angKKhS9KA0-g0xLuMWgnReageYyVnjrusvrc",[709,715,721,727,733,739,745,751,757,763,769,776,782,788,794,800,806,812,817,822,823,829,835,841,847,853,859,865,871,877,883,889,895,901,907,913,919,925,931,937,942,948,954,960,966,972,977,983,989,995,1001,1007,1013,1019,1025,1031,1037,1043,1049,1055,1061,1067,1073,1079,1085,1091,1097,1103,1109,1115,1121,1127,1133,1139,1145,1150,1156,1162,1167,1173,1179,1185,1191,1197,1202,1208,1214,1220,1225,1231],{"path":710,"title":711,"description":712,"published":713,"category":666,"image":714,"draft":670},"\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":716,"title":717,"description":718,"published":719,"category":666,"image":720,"draft":670},"\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":722,"title":723,"description":724,"published":725,"category":666,"image":726,"draft":670},"\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":728,"title":729,"description":730,"published":731,"category":666,"image":732,"draft":670},"\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":734,"title":735,"description":736,"published":737,"category":666,"image":738,"draft":670},"\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":740,"title":741,"description":742,"published":743,"category":666,"image":744,"draft":670},"\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":746,"title":747,"description":748,"published":749,"category":666,"image":750,"draft":670},"\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":752,"title":753,"description":754,"published":755,"category":666,"image":756,"draft":670},"\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":758,"title":759,"description":760,"published":761,"category":666,"image":762,"draft":670},"\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":764,"title":765,"description":766,"published":767,"category":666,"image":768,"draft":670},"\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":770,"title":771,"description":772,"published":773,"category":774,"image":775,"draft":670},"\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":777,"title":778,"description":779,"published":780,"category":666,"image":781,"draft":670},"\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":783,"title":784,"description":785,"published":786,"category":666,"image":787,"draft":670},"\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":789,"title":790,"description":791,"published":792,"category":666,"image":793,"draft":670},"\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":795,"title":796,"description":797,"published":798,"category":666,"image":799,"draft":670},"\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":801,"title":802,"description":803,"published":804,"category":666,"image":805,"draft":670},"\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":807,"title":808,"description":809,"published":810,"category":666,"image":811,"draft":670},"\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":103,"title":813,"description":814,"published":815,"category":666,"image":816,"draft":670},"Supabase \"relation does not exist\" aunque la tabla está ahí","Supabase \"relation does not exist\" o PGRST205: ninguna tabla tiene ese nombre en ese esquema. Cuatro comprobaciones, y lo que abre exponer un esquema.","2026-10-23","\u002Fblog\u002Fsupabase-relation-does-not-exist\u002Fcard-800x500.png",{"path":356,"title":818,"description":819,"published":820,"category":666,"image":821,"draft":670},"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":699,"title":5,"description":669,"published":700,"category":666,"image":687,"draft":670},{"path":824,"title":825,"description":826,"published":827,"category":666,"image":828,"draft":670},"\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":830,"title":831,"description":832,"published":833,"category":666,"image":834,"draft":670},"\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":836,"title":837,"description":838,"published":839,"category":666,"image":840,"draft":670},"\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":842,"title":843,"description":844,"published":845,"category":666,"image":846,"draft":670},"\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":848,"title":849,"description":850,"published":851,"category":666,"image":852,"draft":670},"\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":854,"title":855,"description":856,"published":857,"category":666,"image":858,"draft":670},"\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":860,"title":861,"description":862,"published":863,"category":666,"image":864,"draft":670},"\u002Fblog\u002Fdatabase-error-saving-new-user","\"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":866,"title":867,"description":868,"published":869,"category":666,"image":870,"draft":670},"\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":872,"title":873,"description":874,"published":875,"category":666,"image":876,"draft":670},"\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":878,"title":879,"description":880,"published":881,"category":666,"image":882,"draft":670},"\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":884,"title":885,"description":886,"published":887,"category":666,"image":888,"draft":670},"\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":890,"title":891,"description":892,"published":893,"category":666,"image":894,"draft":670},"\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":896,"title":897,"description":898,"published":899,"category":666,"image":900,"draft":670},"\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":902,"title":903,"description":904,"published":905,"category":666,"image":906,"draft":670},"\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":908,"title":909,"description":910,"published":911,"category":666,"image":912,"draft":670},"\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":914,"title":915,"description":916,"published":917,"category":666,"image":918,"draft":670},"\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":920,"title":921,"description":922,"published":923,"category":666,"image":924,"draft":670},"\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":926,"title":927,"description":928,"published":929,"category":666,"image":930,"draft":670},"\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":932,"title":933,"description":934,"published":935,"category":666,"image":936,"draft":670},"\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":496,"title":938,"description":939,"published":940,"category":666,"image":941,"draft":670},"¿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":943,"title":944,"description":945,"published":946,"category":666,"image":947,"draft":670},"\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":949,"title":950,"description":951,"published":952,"category":666,"image":953,"draft":670},"\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":955,"title":956,"description":957,"published":958,"category":774,"image":959,"draft":670},"\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":961,"title":962,"description":963,"published":964,"category":774,"image":965,"draft":670},"\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":967,"title":968,"description":969,"published":970,"category":774,"image":971,"draft":670},"\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":973,"title":974,"description":975,"published":970,"category":666,"image":976,"draft":670},"\u002Fblog\u002Fsupabase-permission-denied-for-table","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":978,"title":979,"description":980,"published":981,"category":666,"image":982,"draft":670},"\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":984,"title":985,"description":986,"published":987,"category":666,"image":988,"draft":670},"\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":990,"title":991,"description":992,"published":993,"category":774,"image":994,"draft":670},"\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":996,"title":997,"description":998,"published":999,"category":666,"image":1000,"draft":670},"\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":1002,"title":1003,"description":1004,"published":1005,"category":666,"image":1006,"draft":670},"\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":1008,"title":1009,"description":1010,"published":1011,"category":666,"image":1012,"draft":670},"\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":1014,"title":1015,"description":1016,"published":1017,"category":666,"image":1018,"draft":670},"\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":1020,"title":1021,"description":1022,"published":1023,"category":666,"image":1024,"draft":670},"\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":1026,"title":1027,"description":1028,"published":1029,"category":666,"image":1030,"draft":670},"\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":1032,"title":1033,"description":1034,"published":1035,"category":666,"image":1036,"draft":670},"\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":1038,"title":1039,"description":1040,"published":1041,"category":666,"image":1042,"draft":670},"\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":1044,"title":1045,"description":1046,"published":1047,"category":774,"image":1048,"draft":670},"\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":1050,"title":1051,"description":1052,"published":1053,"category":774,"image":1054,"draft":670},"\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":1056,"title":1057,"description":1058,"published":1059,"category":774,"image":1060,"draft":670},"\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":1062,"title":1063,"description":1064,"published":1065,"category":666,"image":1066,"draft":670},"\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":1068,"title":1069,"description":1070,"published":1071,"category":666,"image":1072,"draft":670},"\u002Fblog\u002Fenable-rls-on-every-supabase-table","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":1074,"title":1075,"description":1076,"published":1077,"category":666,"image":1078,"draft":670},"\u002Fblog\u002Fsupabase-rls-disabled-in-public","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":1080,"title":1081,"description":1082,"published":1083,"category":666,"image":1084,"draft":670},"\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":1086,"title":1087,"description":1088,"published":1089,"category":666,"image":1090,"draft":670},"\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":1092,"title":1093,"description":1094,"published":1095,"category":666,"image":1096,"draft":670},"\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":1098,"title":1099,"description":1100,"published":1101,"category":666,"image":1102,"draft":670},"\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":1104,"title":1105,"description":1106,"published":1107,"category":666,"image":1108,"draft":670},"\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":1110,"title":1111,"description":1112,"published":1113,"category":666,"image":1114,"draft":670},"\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":1116,"title":1117,"description":1118,"published":1119,"category":666,"image":1120,"draft":670},"\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":1122,"title":1123,"description":1124,"published":1125,"category":774,"image":1126,"draft":670},"\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":1128,"title":1129,"description":1130,"published":1131,"category":666,"image":1132,"draft":670},"\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":1134,"title":1135,"description":1136,"published":1137,"category":774,"image":1138,"draft":670},"\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":1140,"title":1141,"description":1142,"published":1143,"category":666,"image":1144,"draft":670},"\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":398,"title":1146,"description":1147,"published":1148,"category":666,"image":1149,"draft":670},"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":1151,"title":1152,"description":1153,"published":1154,"category":666,"image":1155,"draft":670},"\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":1157,"title":1158,"description":1159,"published":1160,"category":666,"image":1161,"draft":670},"\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":373,"title":1163,"description":1164,"published":1165,"category":774,"image":1166,"draft":670},"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":1168,"title":1169,"description":1170,"published":1171,"category":774,"image":1172,"draft":670},"\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":1174,"title":1175,"description":1176,"published":1177,"category":666,"image":1178,"draft":670},"\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":1180,"title":1181,"description":1182,"published":1183,"category":666,"image":1184,"draft":670},"\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":1186,"title":1187,"description":1188,"published":1189,"category":774,"image":1190,"draft":670},"\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":1192,"title":1193,"description":1194,"published":1195,"category":666,"image":1196,"draft":670},"\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":1198,"title":1199,"description":1200,"published":1195,"category":666,"image":1201,"draft":670},"\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":1203,"title":1204,"description":1205,"published":1206,"category":666,"image":1207,"draft":670},"\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":1209,"title":1210,"description":1211,"published":1212,"category":774,"image":1213,"draft":670},"\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":1215,"title":1216,"description":1217,"published":1218,"category":666,"image":1219,"draft":670},"\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":1221,"title":1222,"description":1223,"published":1218,"category":774,"image":1224,"draft":670},"\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":1226,"title":1227,"description":1228,"published":1229,"category":774,"image":1230,"draft":670},"\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":1232,"title":1233,"description":1234,"published":1229,"category":666,"image":1235,"draft":670},"\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",1794213645200]