English version: README.en.md
Este proyecto genera archivos JSON desde Google Sheets usando Google Apps Script y los publica automáticamente en un repositorio de GitHub.
El objetivo es evitar que el frontend consulte directamente Google Sheets en cada carga. En su lugar, la web consume un JSON estático, liviano y rápido de leer.
Sheets JSON Publisher convierte datos editables desde Google Sheets en archivos JSON versionados en GitHub.
El flujo general es:
Google Sheets → Apps Script → JSON → GitHub → Frontend
De esta forma, Google Sheets funciona como fuente de datos editable, mientras que GitHub actúa como capa de publicación/entrega del JSON.
Inicialmente, el frontend podía hacer una petición a Google Apps Script para generar el JSON en el momento.
Ese enfoque tenía algunos problemas:
- El usuario final debía esperar la generación del JSON.
- Google Sheets no está pensado como base de datos de alta concurrencia.
- La respuesta podía ser lenta.
- Se podían consumir cuotas o límites de Google Apps Script.
- Cada visita podía disparar una lectura innecesaria a la hoja.
Por eso se cambió el modelo:
Usuario final
↓
Frontend
↓
JSON estático en GitHub
↑
Apps Script publica el JSON
↑
Google Sheets como fuente editable
Así, el usuario no espera la generación de datos, sino que lee un archivo ya preparado.
┌──────────────────┐
│ Google Sheets │
└────────┬─────────┘
│ edición de datos
▼
┌──────────────────┐
│ Apps Script │
│ - lee datos │
│ - arma JSON │
│ - hace commit │
└────────┬─────────┘
│ GitHub API
▼
┌──────────────────┐
│ Repositorio │
│ GitHub │
│ data/*.json │
└────────┬─────────┘
│ lectura pública
▼
┌──────────────────┐
│ Frontend / Web │
└──────────────────┘
- Lee datos desde una o varias hojas de Google Sheets.
- Transforma esos datos en JSON.
- Publica el JSON en este repositorio mediante la API de GitHub.
- Permite actualizaciones:
- manuales mediante botón o menú en Google Sheets,
- programadas cada cierto tiempo,
- automáticas al editar la hoja,
- o por caso específico.
- El frontend consume el JSON desde
raw.githubusercontent.com.
/
├── data/
│ ├── cache.json
│ ├── caso-a.json
│ ├── caso-b.json
│ └── caso-c.json
├── README.md
└── LICENSE
Los archivos JSON generados se guardan principalmente dentro de la carpeta:
data/
Actualmente el frontend consume los datos desde GitHub Raw:
https://raw.githubusercontent.com/TU_USUARIO/TU_REPO/main/data/cache.json
Ejemplo en JavaScript:
const JSON_URL =
'https://raw.githubusercontent.com/TU_USUARIO/TU_REPO/main/data/cache.json';
fetch(JSON_URL)
.then(response => response.json())
.then(data => {
console.log(data);
})
.catch(error => {
console.error('Error cargando el JSON:', error);
});Ejemplo con async/await:
const JSON_URL =
'https://raw.githubusercontent.com/TU_USUARIO/TU_REPO/main/data/cache.json';
async function loadData() {
try {
const response = await fetch(JSON_URL);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
console.log('Actualizado:', data.generatedAt);
console.log('Elementos:', data.count);
return data.items;
} catch (error) {
console.error('No se pudo cargar el JSON:', error);
return [];
}
}
loadData();Cada archivo JSON puede incluir metadatos básicos y los datos principales.
Ejemplo:
{
"generatedAt": "2026-01-01T12:00:00.000Z",
"source": "Google Sheets",
"count": 2,
"items": [
{
"id": 1,
"nombre": "Ejemplo A",
"estado": "activo"
},
{
"id": 2,
"nombre": "Ejemplo B",
"estado": "inactivo"
}
]
}Campos principales:
| Campo | Descripción |
|---|---|
generatedAt |
Fecha y hora de generación del JSON. |
source |
Origen de los datos. |
count |
Cantidad de elementos generados. |
items |
Arreglo con los datos principales. |
Crea un repositorio donde se publicarán los JSON.
Por ejemplo:
https://github.com/TU_USUARIO/TU_REPO
Dentro del repositorio se espera una carpeta:
data/
La hoja de cálculo es la fuente de datos.
Ejemplo de estructura:
| id | nombre | estado |
|---|---|---|
| 1 | Ejemplo A | activo |
| 2 | Ejemplo B | inactivo |
La primera fila se usa como encabezados.
Cada fila se convierte en un objeto JSON.
Ejemplo:
{
"id": 1,
"nombre": "Ejemplo A",
"estado": "activo"
}El script debe estar vinculado a Google Sheets o tener acceso a la hoja correspondiente.
Apps Script se encarga de:
- Leer la hoja.
- Convertir los datos a JSON.
- Enviar el archivo a GitHub usando la API de Contents.
Se requiere un token de GitHub con permisos para escribir en este repositorio.
Recomendación:
- Usar un token de acceso fino/granular.
- Dar permiso solo al repositorio necesario.
- Dar permiso de lectura/escritura sobre contenidos.
- No exponer el token en el frontend.
- No subir el token al repositorio.
En Apps Script, guarda el token como una propiedad del script.
Ruta:
Apps Script → Project Settings → Script Properties
Agrega una propiedad:
| Nombre | Valor |
|---|---|
GITHUB_TOKEN |
token de GitHub |
Ejemplo:
GITHUB_TOKEN = ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
⚠️ No coloques el token directamente en el código.
- El token de GitHub se guarda en Apps Script como Script Property.
- El token no debe exponerse en el frontend.
- El token no debe publicarse en el repositorio.
- Si el JSON contiene información sensible, no debería publicarse en un repositorio público.
Para datos privados, considerar:
- repositorio privado con backend autenticado,
- Cloudflare Access,
- Supabase,
- Firebase,
- API propia,
- signed URLs,
- u otro sistema de control de acceso.
La publicación puede ejecutarse mediante triggers de Google Apps Script.
Se puede programar un trigger para ejecutar la publicación cada cierto tiempo.
Ejemplos:
cada 1 hora
cada 3 horas
cada 6 horas
cada 12 horas
cada 24 horas
También se puede usar un trigger instalable onEdit.
Para evitar publicaciones excesivas, se recomienda usar debounce.
Ejemplo conceptual:
Usuario edita hoja
↓
Apps Script detecta cambio
↓
Espera 30-60 segundos
↓
Si no hay más cambios, publica JSON
Esto evita publicar por cada tecla presionada.
La publicación también puede ejecutarse manualmente desde Google Sheets.
Opciones:
- menú personalizado,
- botón insertado como imagen o dibujo,
- funciones separadas por caso.
Ejemplo de menú:
Publicar JSON
├── Publicar todo
├── Publicar caso A
├── Publicar caso B
└── Publicar caso C
Esto permite generar archivos específicos sin actualizar todo el contenido.
Si existen varios casos o categorías, se pueden generar archivos separados:
data/cache.json
data/caso-a.json
data/caso-b.json
data/caso-c.json
Ejemplo de consumo:
fetch('https://raw.githubusercontent.com/TU_USUARIO/TU_REPO/main/data/caso-a.json')
.then(response => response.json())
.then(console.log);const DATA_URL =
'https://raw.githubusercontent.com/TU_USUARIO/TU_REPO/main/data/cache.json';
async function fetchData() {
try {
const response = await fetch(DATA_URL);
if (!response.ok) {
throw new Error(`Error HTTP: ${response.status}`);
}
const data = await response.json();
renderData(data.items);
} catch (error) {
console.error('Error al obtener datos:', error);
}
}
function renderData(items) {
const container = document.getElementById('app');
if (!container) return;
container.innerHTML = '';
items.forEach(item => {
const card = document.createElement('div');
card.className = 'card';
card.textContent = item.nombre || 'Sin nombre';
container.appendChild(card);
});
}
fetchData();Actualmente el proyecto consume los JSON directamente desde:
raw.githubusercontent.com
Esta solución es suficiente por ahora porque los archivos JSON son livianos y la lectura es rápida.
Más adelante se podría evaluar el uso de un CDN como:
- Cloudflare Workers,
- Cloudflare Pages,
- jsDelivr,
- Vercel Edge Functions,
- Netlify Edge Functions,
- u otra plataforma de cacheo.
Esto permitiría mejorar:
- velocidad,
- control de caché,
- invalidación,
- disponibilidad,
- headers HTTP,
- y estrategias como
stale-while-revalidate.
raw.githubusercontent.compuede aplicar caché durante algunos minutos.- La actualización no es necesariamente instantánea.
- Google Apps Script tiene límites de ejecución y cuotas.
- Google Sheets no es ideal como base de datos de alta concurrencia.
- Si el volumen de datos crece mucho, conviene migrar a otra solución.
- Google Sheets
- Google Apps Script
- GitHub API
- GitHub Raw
- JavaScript
- JSON
Verifica que Apps Script haya hecho commit correctamente.
Revisa:
data/cache.json
También revisa el historial de commits del repositorio.
Posibles causas:
- token inválido,
- token vencido,
- token sin permisos sobre el repositorio,
- token sin permiso de escritura,
- nombre de usuario, repo o rama incorrectos.
Revisa la propiedad:
GITHUB_TOKEN
Verifica:
- si el trigger está instalado,
- si la función de publicación se ejecuta,
- si hay errores en las ejecuciones de Apps Script,
- si la hoja tiene datos válidos,
- si GitHub está recibiendo el commit.
Puede ser caché de:
- navegador,
- GitHub Raw,
- service worker,
- CDN,
- caché HTTP local.
Para pruebas, puedes usar DevTools con caché deshabilitada.
Más adelante, si se usa un CDN, se podrá implementar invalidación o versionado por commit.
Próximas mejoras posibles:
- Usar jsDelivr como CDN.
- Usar Cloudflare Workers para cacheo inteligente.
- Implementar
stale-while-revalidate. - Publicar un endpoint de estado.
- Generar versiones por commit para invalidar caché.
- Agregar logs de publicación.
- Agregar validación de datos antes de publicar.
- Agregar soporte para múltiples hojas.
- Agregar soporte para múltiples archivos.
- Migrar a Supabase o Firebase si se necesita lectura en tiempo real.
- Usar GitHub Actions como pipeline alternativo.
Este proyecto está bajo licencia MIT, salvo que se indique lo contrario.
Este proyecto usa Google Sheets como fuente editable, Google Apps Script como generador de JSON y GitHub como almacenamiento/entrega de archivos estáticos.