TL;DR — Resumen Rápido

Cloudflare Workers ejecuta JavaScript en el edge con aislamientos V8. Despliega tu primer Worker con Wrangler, KV, secretos y CI/CD con GitHub Actions.

Cloudflare Workers lleva la computación serverless al edge — tu código se ejecuta en centros de datos distribuidos en más de 300 ciudades del mundo, a milisegundos de tus usuarios, sin necesidad de aprovisionar ni escalar servidores. Construido sobre aislamientos V8 (el mismo motor que impulsa Chrome y Node.js), Workers logra tiempos de arranque en frío menores a 1 milisegundo, siendo dramáticamente más rápido que las funciones serverless tradicionales basadas en contenedores. En esta guía irás de cero a un Worker completamente desplegado en producción con almacenamiento KV, secretos cifrados, dominios personalizados y un pipeline de despliegue automatizado con GitHub Actions.

Requisitos Previos

  • Node.js 18+ instalado localmente
  • Una cuenta de Cloudflare (el nivel gratuito funciona para todo en esta guía)
  • Familiaridad básica con JavaScript o TypeScript
  • Un dominio registrado añadido a Cloudflare (requerido solo para la sección de dominio personalizado)
  • npm o pnpm como gestor de paquetes

Creando tu Primer Worker

Instala Wrangler, la CLI oficial de Cloudflare, globalmente:

npm install -g wrangler
wrangler login

El comando wrangler login abre una ventana del navegador y solicita que autorices a Wrangler a acceder a tu cuenta de Cloudflare. Tras la autorización, las credenciales se almacenan en ~/.wrangler/config/default.toml.

Crea un nuevo proyecto:

wrangler init my-api-worker
cd my-api-worker

Wrangler presenta algunas preguntas — elige TypeScript y la plantilla “Hello World” Worker. La estructura del directorio resultante es:

my-api-worker/
  src/
    index.ts          ← tu código Worker
  wrangler.toml       ← configuración del proyecto
  package.json
  tsconfig.json

Entendiendo wrangler.toml

El archivo wrangler.toml es el manifiesto del proyecto. Una configuración mínima luce así:

name = "my-api-worker"
main = "src/index.ts"
compatibility_date = "2024-09-23"

[[routes]]
pattern = "api.example.com/*"
zone_name = "example.com"

Campos clave:

CampoPropósito
nameNombre del Worker mostrado en el panel
mainArchivo de entrada resuelto por Wrangler
compatibility_dateFija el comportamiento de la API del runtime a una fecha específica
routesMapea patrones de URL a este Worker
[[kv_namespaces]]Vincula namespaces KV como variables de entorno
[vars]Variables de entorno en texto plano

La compatibility_date es importante — Cloudflare ocasionalmente introduce cambios que rompen la compatibilidad en las APIs de Worker, y esta fecha fija qué conjunto de APIs ve tu Worker. Siempre establécela en la fecha de creación del proyecto y actualízala explícitamente tras revisar el registro de cambios.

Escribiendo el Worker

El punto de entrada del Worker exporta un objeto predeterminado con un manejador fetch. Cada solicitud HTTP entrante llama a este manejador:

export interface Env {
  MY_KV: KVNamespace;
  API_SECRET: string;
}

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const url = new URL(request.url);

    // Ruta: retornar JSON
    if (url.pathname === '/api/status') {
      return Response.json({ status: 'ok', region: request.cf?.colo });
    }

    // Ruta: retornar HTML
    if (url.pathname === '/') {
      return new Response(
        `<h1>¡Hola desde el edge!</h1>`,
        { headers: { 'Content-Type': 'text/html;charset=UTF-8' } }
      );
    }

    return new Response('No Encontrado', { status: 404 });
  }
};

Patrones de Request y Response

Workers usa la API Fetch estándar — Request, Response y Headers son idénticos a las APIs del navegador. Esto significa que el código que escribes para Workers es en gran parte portable.

Retornar JSON es idiomático con Response.json():

return Response.json({ items: ['a', 'b', 'c'] }, {
  headers: { 'Cache-Control': 'public, max-age=60' }
});

Redirigir una solicitud:

return Response.redirect('https://example.com/nueva-ruta', 301);

Modificar una respuesta proxiada (el patrón “transform”):

const upstream = await fetch(request);
const body = await upstream.text();
return new Response(body.replace('texto antiguo', 'texto nuevo'), upstream);

Desarrollo Local

Ejecuta el servidor de desarrollo local:

wrangler dev

Wrangler inicia un servidor local en http://localhost:8787 que emula el runtime de Cloudflare incluyendo KV, Durable Objects, R2 y el objeto de metadatos request.cf. La recarga en caliente se activa automáticamente ante cambios en los archivos.

Para probar contra la infraestructura real de Cloudflare (útil para Workers que llaman a otros servicios de Cloudflare):

wrangler dev --remote

Inspecciona el tráfico en vivo con el log tail integrado en una segunda terminal:

wrangler tail

wrangler tail transmite logs en tiempo real del Worker de producción, mostrando detalles de solicitud/respuesta, salida de consola y excepciones.

Desplegando a Producción

Despliega con un solo comando:

wrangler deploy

Wrangler compila tu TypeScript, empaqueta las dependencias y sube el Worker a la red de Cloudflare. El despliegue se propaga globalmente en segundos. La salida incluye la URL de workers.dev:

Published my-api-worker (2.45 sec)
  https://my-api-worker.tu-subdominio.workers.dev

Conectando un Dominio Personalizado

Agrega un bloque routes en wrangler.toml para enrutar solicitudes de tu dominio proxiado por Cloudflare al Worker:

[[routes]]
pattern = "api.example.com/*"
zone_name = "example.com"

Alternativamente, usa la función Custom Domains (sin necesidad de patrón de ruta):

[[routes]]
pattern = "api.example.com"
custom_domain = true

Custom Domains provisiona automáticamente un certificado TLS y maneja todo el enrutamiento.

Variables de Entorno y Secretos

Variables Simples

La configuración no sensible vive en wrangler.toml:

[vars]
ENVIRONMENT = "production"
MAX_RETRIES = "3"

Accede a ellas en el Worker como env.ENVIRONMENT y env.MAX_RETRIES.

Secretos Cifrados

Los secretos se cifran en reposo y nunca son visibles tras la carga. Agrégalos mediante la CLI:

wrangler secret put API_KEY

Wrangler te pedirá que ingreses el valor de forma interactiva. Lista los secretos existentes:

wrangler secret list

En el Worker, los secretos aparecen como env.API_KEY — indistinguibles de las variables simples en tiempo de ejecución pero almacenados cifrados en la bóveda de Cloudflare.

Almacenamiento KV

KV (Workers KV) es un almacén clave-valor distribuido globalmente con consistencia eventual. Destaca para almacenar configuración, sesiones de usuario, respuestas de API en caché y banderas de características.

Crea un namespace KV:

wrangler kv namespace create CACHE

Wrangler devuelve el ID del namespace. Agrega el binding a wrangler.toml:

[[kv_namespaces]]
binding = "CACHE"
id = "abc123def456..."

Escribe y lee desde KV en el Worker:

// Escribir (con TTL opcional en segundos)
await env.CACHE.put('usuario:123', JSON.stringify(datosUsuario), { expirationTtl: 3600 });

// Leer
const raw = await env.CACHE.get('usuario:123');
const usuario = raw ? JSON.parse(raw) : null;

// Eliminar
await env.CACHE.delete('usuario:123');

Comparativa

CaracterísticaCloudflare WorkersAWS Lambda@EdgeDeno Deploy
RuntimeAislamientos V8Contenedor Node.jsAislamientos V8
Arranque en frío< 1 ms100–500 ms~50 ms
PoPs globales300+~4 regiones CloudFront35 regiones
Nivel gratuito100k req/díaPago por solicitud100k req/día
Tiempo CPU máx.50 ms (gratis) / 30 s (pago)30 s50 ms
AlmacenamientoKV, R2, D1, Durable ObjectsDynamoDB (separado)Deno KV
TypeScriptNativo (integrado)Mediante paso de buildNativo
Precio (más del gratis)$0,50/millón solicitudes~$0,60/millón + Lambda$0,50/millón solicitudes

Workers gana en arranque en frío y distribución global. Lambda@Edge es la elección correcta si ya estás profundamente integrado en el ecosistema AWS. Deno Deploy es una buena alternativa si quieres el modelo de permisos de Deno.

Escenario del Mundo Real: Worker Proxy de API

Tienes una API de terceros que no soporta CORS, requiere una clave de API que no puedes exponer al navegador, y responde con datos que quieres cachear y transformar. Un Cloudflare Worker es la solución perfecta.

export interface Env {
  UPSTREAM_API_KEY: string;
  CACHE: KVNamespace;
}

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const url = new URL(request.url);
    const upstreamUrl = `https://api.terceros.com${url.pathname}${url.search}`;
    const cacheKey = upstreamUrl;

    // Verificar caché KV primero
    const cached = await env.CACHE.get(cacheKey);
    if (cached) {
      return Response.json(JSON.parse(cached), {
        headers: { 'X-Cache': 'HIT', 'Access-Control-Allow-Origin': '*' }
      });
    }

    // Obtener del upstream con clave secreta
    const upstream = await fetch(upstreamUrl, {
      headers: { 'Authorization': `Bearer ${env.UPSTREAM_API_KEY}` }
    });

    if (!upstream.ok) {
      return new Response('Error upstream', { status: upstream.status });
    }

    const data = await upstream.json();

    // Cachear la respuesta por 5 minutos
    ctx.waitUntil(env.CACHE.put(cacheKey, JSON.stringify(data), { expirationTtl: 300 }));

    return Response.json(data, {
      headers: { 'X-Cache': 'MISS', 'Access-Control-Allow-Origin': '*' }
    });
  }
};

Este Worker mantiene la clave de API en el servidor, agrega encabezados CORS que la API original no tiene, y sirve respuestas cacheadas desde las ubicaciones edge más cercanas a cada usuario.

CI/CD con GitHub Actions

Automatiza los despliegues en cada push a main:

# .github/workflows/deploy.yml
name: Deploy Worker

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    name: Deploy
    steps:
      - uses: actions/checkout@v4

      - name: Configurar Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '22'
          cache: 'npm'

      - name: Instalar dependencias
        run: npm ci

      - name: Desplegar a Cloudflare Workers
        uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CF_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}

Agrega CF_API_TOKEN y CLOUDFLARE_ACCOUNT_ID como secretos del repositorio en GitHub. Crea el token de API en el panel de Cloudflare bajo Perfil → Tokens de API → Crear Token usando la plantilla “Edit Cloudflare Workers”.

Errores Comunes y Casos Especiales

Límite de tiempo CPU — El plan gratuito permite 10 ms de tiempo CPU por solicitud. El plan Workers Paid sube esto a 30 segundos. El trabajo intensivo en CPU puede alcanzar este límite; descarga los cálculos pesados a una cola o usa Durable Objects.

Límites de sub-solicitudes — Cada invocación de Worker puede hacer hasta 50 llamadas fetch() salientes en el plan gratuito (1000 en el de pago).

Fecha de compatibilidad y cambios que rompen la compatibilidad — Al actualizar compatibility_date, revisa el registro de cambios de las banderas de compatibilidad. Algunos indicadores cambian cómo funcionan Request.clone(), los streams o el manejo de errores.

Límites de tamaño — Los scripts Worker están limitados a 1 MB tras la compresión (10 MB en el plan de pago). Las dependencias npm grandes pueden superar este límite; usa tree shaking y evita empaquetar módulos exclusivos del lado del servidor.

Sin acceso al sistema de archivos — Los Workers no tienen E/S de disco. Toda la persistencia debe ir a través de KV, R2, D1 o Durable Objects.

waitUntil para trabajo en segundo plano — Usa ctx.waitUntil(promise) para trabajo que debe completarse después de enviar la respuesta (como escrituras en caché).

Solución de Problemas

Error: Script startup exceeded CPU time limit — Tu Worker está realizando trabajo costoso durante la inicialización del módulo (en el nivel superior, fuera del manejador). Mueve las operaciones costosas dentro del manejador o usa inicialización lazy.

TypeError: Cannot read properties of undefined (reading 'get') — Un binding KV u otro binding falta en wrangler.toml, o estás accediendo a env.MI_BINDING antes de que el binding esté configurado.

wrangler deploy falla con “Authentication error” — Tu sesión de Wrangler ha expirado. Ejecuta wrangler login nuevamente o establece la variable de entorno CLOUDFLARE_API_TOKEN para entornos CI.

Errores CORS en el navegador — La respuesta del Worker no tiene Access-Control-Allow-Origin. Agrega el encabezado en tu constructor de Response o usa un helper. También maneja la solicitud de preflight OPTIONS por separado.

El dominio personalizado no enruta al Worker — Asegúrate de que el dominio esté proxiado a través de Cloudflare (nube naranja en la configuración DNS), el patrón de ruta en wrangler.toml usa comodín /* si es necesario, y has vuelto a desplegar después de cambiar wrangler.toml.

Resumen

  • Cloudflare Workers son funciones serverless basadas en aislamientos V8 que corren en el edge con arranques en frío por debajo del milisegundo y distribución global
  • Instala Wrangler con npm install -g wrangler, crea el scaffold con wrangler init y prueba localmente con wrangler dev
  • El archivo wrangler.toml controla el nombre del Worker, el punto de entrada, la fecha de compatibilidad, las rutas y todos los bindings de recursos
  • Usa wrangler secret put para valores sensibles y [vars] en wrangler.toml para configuración no sensible
  • Los namespaces KV ofrecen almacenamiento clave-valor distribuido globalmente, accesible mediante env.BINDING.get/put/delete
  • Workers supera a Lambda@Edge en arranque en frío y alcance global; Lambda@Edge es preferible para integración profunda con el ecosistema AWS
  • La Action wrangler-action de GitHub proporciona CI/CD listo para usar; delimita tu token de API solo a permisos de Workers
  • Vigila los límites de tiempo CPU, los topes de sub-solicitudes y el límite de 1 MB de tamaño de script en el plan gratuito

Artículos Relacionados