Despliegue en Cloudflare Workers
vinext deploy
El comando vinext deploy automatiza todo el proceso de despliegue en Cloudflare Workers:
vinext deploy
¿Qué hace internamente?
El proceso tiene 7 pasos:
- Detección de proyecto — Identifica App Router vs Pages Router, ISR, MDX, módulos nativos
- Gestión de dependencias — Instala
@cloudflare/vite-plugin,wrangler, y@vitejs/plugin-rscsi faltan - Preparación de módulos — Agrega
"type": "module", renombra configs CJS a.cjs - Generación de configuración — Crea
wrangler.jsonc,worker/index.ts,vite.config.tssi no existen - Build de Vite — Compila la aplicación
- TPR (opcional) — Pre-renderiza páginas de alto tráfico
- Deploy con Wrangler — Sube el worker a Cloudflare
Opciones del deploy
vinext deploy --preview # Deploy a entorno preview
vinext deploy --env staging # Deploy a entorno específico
vinext deploy --name mi-app # Nombre custom del worker
vinext deploy --skip-build # Solo deploy (asume build previo)
vinext deploy --dry-run # Simula sin desplegar
Configuración manual
wrangler.jsonc
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "mi-app-vinext",
"compatibility_date": "2026-02-12",
"compatibility_flags": ["nodejs_compat"],
"main": "./worker/index.ts",
"preview_urls": true,
"assets": {
"not_found_handling": "none",
"binding": "ASSETS"
}
}
vite.config.ts para Workers
App Router:
import { defineConfig } from "vite";
import vinext from "vinext";
import { cloudflare } from "@cloudflare/vite-plugin";
export default defineConfig({
plugins: [
vinext(),
cloudflare({
viteEnvironment: {
name: "rsc",
childEnvironments: ["ssr"],
},
}),
],
});
Pages Router:
import { defineConfig } from "vite";
import vinext from "vinext";
import { cloudflare } from "@cloudflare/vite-plugin";
export default defineConfig({
plugins: [vinext(), cloudflare()],
});
Worker entry point
// worker/index.ts (App Router)
import handler from "vinext/server/app-router-entry";
export default handler;
Si no necesitas lógica custom en el worker, puedes apuntar directamente en wrangler:
{
"main": "vinext/server/app-router-entry"
}
Worker con optimización de imágenes
// worker/index.ts
import handler from "vinext/server/app-router-entry";
interface Env {
ASSETS: Fetcher;
IMAGES: { input: any; transform: any; output: any };
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
if (url.pathname === "/_vinext/image") {
const src = url.searchParams.get("url");
const width = parseInt(url.searchParams.get("w") || "0");
const quality = parseInt(url.searchParams.get("q") || "75");
const asset = await env.ASSETS.fetch(new URL(src!, request.url));
const result = await env.IMAGES
.input(asset)
.transform({ width, quality, format: "auto" })
.output();
return new Response(result.image(), {
headers: { ...result.headers, "cache-control": "public, max-age=31536000" },
});
}
return handler.fetch(request);
},
};
KV Cache Handler (ISR)
Para Incremental Static Regeneration en Workers, usa el KV Cache Handler:
Configurar KV namespace
En wrangler.jsonc:
{
"kv_namespaces": [
{ "binding": "CACHE", "id": "tu-kv-namespace-id" }
]
}
Activar en el worker
import { KVCacheHandler } from "vinext/cloudflare";
import { setCacheHandler } from "next/cache";
// En el worker entry
setCacheHandler(new KVCacheHandler(env.CACHE));
Cómo funciona el KV Cache
| Característica | Comportamiento |
|---|---|
| Stale-while-revalidate | Retorna datos stale mientras regenera en background |
| Deduplicación | Una sola regeneración concurrente por key |
| Invalidación por tags | revalidateTag() borra la entry del cache |
| TTL | 10x el periodo de revalidación (entre 60s y 30 días) |
ISR en acción
// app/posts/page.tsx
export const revalidate = 60; // Regenerar cada 60 segundos
export default async function PostsPage() {
const posts = await fetch("https://api.example.com/posts", {
next: { revalidate: 60 },
}).then(r => r.json());
return (
<ul>
{posts.map((p: { id: number; title: string }) => (
<li key={p.id}>{p.title}</li>
))}
</ul>
);
}
La primera visita renderiza la página y la guarda en KV. Las siguientes 60 segundos sirven desde cache. Después, la siguiente visita dispara regeneración en background.
Traffic-aware Pre-Rendering (TPR)
TPR pre-renderiza las páginas más visitadas al momento del deploy, basándose en analytics de Cloudflare:
vinext deploy --experimental-tpr
Opciones de TPR
vinext deploy --experimental-tpr --tpr-coverage 95 # Cubrir 95% del tráfico
vinext deploy --experimental-tpr --tpr-limit 500 # Máximo 500 páginas
vinext deploy --experimental-tpr --tpr-window 48 # Últimas 48h de analytics
Requisitos
- Dominio custom configurado en Cloudflare
CLOUDFLARE_API_TOKENcon permisoZone.Analytics(read)- La app debe estar desplegada previamente (necesita datos de tráfico)
Cómo funciona
- Consulta analytics de la zona al momento del deploy
- Identifica las páginas con más tráfico
- Pre-renderiza solo esas páginas
- Sube el HTML pre-renderizado a KV
El resultado: latencia nivel SSG para las páginas populares, sin pre-renderizar todo el sitio.
Variables de entorno
Las variables NEXT_PUBLIC_* se inlinan en build time:
# .env.local
NEXT_PUBLIC_API_URL=https://api.miapp.com
SECRET_KEY=mi-secreto
En el código:
process.env.NEXT_PUBLIC_API_URL— disponible en cliente y servidorprocess.env.SECRET_KEY— solo disponible en el servidor
Para Workers, configura secrets con wrangler:
wrangler secret put SECRET_KEY
Siguiente paso
En el último capítulo aprenderemos a migrar un proyecto Next.js existente a Vinext.