Parte de la guía Static Snapshot con TanStack Start + Hono. Construida sobre la
Parte 1. Aquí añadimos un endpoint de refresh protegido con secreto que recarga el contenido en caliente, sin rebuild. Lo dispara el webhook de Content Island cada vez que publicas: llama a /api/refresh en el api desplegado, que ejecuta refreshSnapshot() y trae contenido fresco del API de Content Island.
El snapshot que va incrustado en el bundle (Parte 1) sigue siendo la semilla inicial. Con un token de lectura, el refresh actualiza ese contenido en memoria sin volver a desplegar.
El backend Hono añade una ruta que valida un secreto compartido antes de refrescar. Con CONTENT_ISLAND_READ_TOKEN puesto, el loader pide el contenido en vivo; sin él, sirve el snapshot del bundle (como en la Parte 1).
// apps/api/src/app.ts (resumido)
app.on(['GET', 'POST'], '/api/refresh', async (c) => {
const secret = process.env.REFRESH_SECRET;
const provided = c.req.header('x-refresh-secret') ?? c.req.query('secret');
if (!secret || provided !== secret) {
return c.json({ error: 'UNAUTHORIZED' }, 401);
}
return c.json(await contentIslandClient.refreshSnapshot());
});- En vercel.com → Add New… → Project e importa tu repositorio de GitHub.
- En Root Directory pulsa Edit y elige la carpeta del api:
02-cd-workflow/apps/api(es un monorepo; Vercel debe apuntar a esa subcarpeta). - Framework Preset:
Other. No hace falta build command: Vercel detecta la carpetaapi/como función serverless y aplica elvercel.json.
// apps/api/api/index.ts — adaptador de Hono a la función de Vercel
import { getRequestListener } from '@hono/node-server';
import app from '../src/app.js';
export default getRequestListener(app.fetch);// apps/api/vercel.json — manda todas las rutas al handler
{ "rewrites": [{ "source": "/(.*)", "destination": "/api" }] }-
En Environment Variables añade estas dos (para Production y Preview):
Name Value CONTENT_ISLAND_READ_TOKENtu token de lectura de Content Island (necesario para que el refresh traiga contenido en vivo) REFRESH_SECRETun secreto que inventas tú (p. ej. openssl rand -hex 24) -
Pulsa Deploy. Al terminar tendrás una URL tipo
https://tu-api.vercel.app. Compruébalo:
curl https://tu-api.vercel.app/health # -> {"ok":true}Tu endpoint de refresh será: https://tu-api.vercel.app/api/refresh. Guárdalo para el paso 3.
Si cambias las env vars en Vercel más adelante, haz redeploy para que la función use los nuevos valores.
El workflow .github/workflows/refresh.yml hace un curl a tu endpoint. Lee sus datos de los secrets del repositorio.
Ve a tu repo → Settings → Secrets and variables → Actions → New repository secret y crea:
| Secret | Valor |
|---|---|
REFRESH_URL |
https://tu-api.vercel.app/api/refresh |
REFRESH_SECRET |
el mismo secreto que pusiste en Vercel |
Puedes probar el workflow a mano antes de conectar Content Island: repo → pestaña Actions → refresh.yml → Run workflow (workflow_dispatch). Debe terminar en verde y mostrar HTTP 200.
Content Island dispara el workflow llamando a la API de repository dispatch de GitHub. Para autenticarse necesita un token de GitHub.
- GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens → Generate new token.
- Repository access: Only select repositories → elige este repositorio.
- Permissions → Repository permissions → Contents: Read and write (es el permiso que necesita la API de repository dispatch).
- Generate token y cópialo (solo se muestra una vez).
En tu proyecto de Content Island → sección Webhooks → nuevo webhook de GitHub. Te pedirá:
| Campo | Valor |
|---|---|
| Propietario del repositorio | Tu usuario u organización donde este el repositorio |
| Nombre del repositorio | Nombre del repositorio |
| Event name | content-refresh |
El event name tiene que coincidir con el types: del workflow (refresh.yml escucha
content-refresh).
Equivalente en crudo (útil para probarlo con
curl): Content Island hace este POST autenticado a la API de GitHub.curl -X POST https://api.github.com/repos/<owner>/<repo>/dispatches \ -H "Authorization: Bearer <TU_PAT_DE_GITHUB>" \ -H "Accept: application/vnd.github+json" \ -d '{"event_type":"content-refresh"}'
export REFRESH_SECRET=dev-secret
# opcional: export CONTENT_ISLAND_READ_TOKEN=... para pedir el contenido en vivo
npm install
npm run dev # api :3001 · web :3000Simula lo que hará el webhook y comprueba la protección del secreto:
curl -X POST http://localhost:3001/api/refresh \
-H "x-refresh-secret: $REFRESH_SECRET"
# -> {"status":"updated"|"unchanged", ...}
curl -i -X POST http://localhost:3001/api/refresh # sin secreto -> 401Publica algo en Content Island, relanza el curl y recarga http://localhost:3000: el
exportedAt de la home habrá cambiado, sin reiniciar ni rebuild.
- Prueba el workflow a mano: repo → Actions →
refresh.yml→ Run workflow. Debe darHTTP 200. - Prueba el webhook real: publica un contenido en Content Island y verás una ejecución nueva del workflow disparada por
repository_dispatch. - Comprueba que refresca: recarga tu web y mira que el
exportedAtse ha actualizado.
Si algo falla, mira en orden: el log del workflow en Actions, los Runtime Logs de la función en Vercel, y que REFRESH_URL, REFRESH_SECRET y el token coincidan en todos los sitios.
- Content Island — GitHub Webhooks ·
Snapshot mode ·
refreshSnapshot() - Hono en Vercel ·
GitHub ·
repository_dispatch