wrangler.jsonc: el manifiesto del Worker
El manifiesto como fuente única de la verdad: los campos de identidad (name, main, compatibility_date), los bindings como capacidades declaradas que llegan por env, los entornos y su herencia, y por qué jsonc reemplazó al viejo toml.
Un Worker es dos cosas: el código que ejecutas y el contrato de todo lo que ese código puede tocar. Ese contrato vive en wrangler.jsonc, el manifiesto que declara cómo se llama el Worker, dónde está su punto de entrada, contra qué versión del runtime se comporta y a qué bases de datos, buckets o colas tiene derecho a acceder. No es un archivo de configuración accesorio: es la frontera formal entre tu compute y la plataforma, y aprender a leerlo es aprender a leer la arquitectura del proyecto de un vistazo.
- Entender el manifiesto como la fuente única de verdad del Worker, versionada en git.
- Dominar los campos de identidad:
name,main,compatibility_dateycompatibility_flags. - Leer la sección de bindings como capacidades declaradas que llegan por
env. - Manejar entornos con
env.<nombre>y saber qué se hereda y qué no; entender por qué jsonc reemplazó al toml.
Config como código: la fuente de la verdad
El manifiesto encarna un principio: la infraestructura no es un panel que alguien tocó una vez, sino un archivo cuya historia cuenta git. Todo lo que define al Worker —su identidad, su runtime, sus permisos— está en un solo lugar, revisable en un pull request y reproducible en cualquier máquina. wrangler lo detecta automáticamente en la raíz del proyecto, y un editor con soporte de esquema te autocompleta y valida cada campo si añades la línea $schema, que apunta al esquema que wrangler trae consigo.
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "mi-worker",
"main": "src/index.ts",
"compatibility_date": "2026-01-15",
"compatibility_flags": ["nodejs_compat"],
"observability": { "enabled": true }
}
Los campos de identidad
Cuatro campos definen qué es el Worker antes de hablar de recursos. name es su identificador: da nombre al script en tu cuenta y, salvo dominio propio, al subdominio en workers.dev. main apunta al punto de entrada que wrangler empaqueta —normalmente src/index.ts—; a partir de ahí, el bundler sigue los import y arma un único módulo.
Los dos campos de compatibilidad son los más profundos y los más incomprendidos. compatibility_date fija una fecha, y esa fecha congela el comportamiento del runtime: workerd decide qué APIs y qué semántica te ofrece según el día que declaraste, no según el día que despliegas. Es control de versiones del runtime por fecha. compatibility_flags permite activar o desactivar cambios puntuales sin mover toda la fecha —el caso más común, nodejs_compat, habilita un subconjunto de APIs de Node.
Alrededor de ese núcleo, un puñado de campos gobierna dónde y cómo se sirve el Worker: workers_dev decide si se expone en el subdominio gratuito workers.dev, routes lo engancha a un dominio propio, triggers define horarios cron, y placement activa el Smart Placement para acercar el compute a los datos. El account_id es opcional en el archivo —conviene dejarlo fuera y pasarlo por variable de entorno para no fijar una cuenta en el repositorio—.
Es tentador poner una fecha vieja y olvidarla, pero esa fecha gobierna la conducta real de tu Worker. Dejarla estancada te ancla a comportamientos antiguos y te priva de correcciones; adelantarla de golpe puede alterar sutilmente cómo se resuelve una API de la que dependías. La disciplina correcta es tratarla como una dependencia más: actualízala de forma consciente, lee el registro de cambios del runtime y valida en wrangler dev antes de llevarla a producción. Una fecha bien elegida es lo que hace que un redeploy dentro de dos años se comporte igual que hoy.
Bindings: capacidades declaradas
Aquí está el modelo mental que distingue a Cloudflare. Un Worker no accede a una base de datos con una cadena de conexión llena de secretos: declara un binding en el manifiesto, y el runtime inyecta ese recurso en el objeto env que recibe cada handler. Declaras una capacidad; recibes un objeto ya conectado. No hay credenciales que filtrar porque no hay credenciales: hay derechos concedidos en la config.
{
"vars": { "ENTORNO": "produccion" },
"kv_namespaces": [
{ "binding": "CACHE", "id": "0f2ac..." }
],
"d1_databases": [
{ "binding": "DB", "database_name": "app", "database_id": "8c1e..." }
],
"r2_buckets": [
{ "binding": "MEDIA", "bucket_name": "uploads" }
],
"ai": { "binding": "AI" }
}
Cada entrada tiene un binding: el nombre bajo el que aparece en env. La declaración de arriba hace que tu código pueda usar env.CACHE, env.DB, env.MEDIA y env.AI directamente, ya tipados si corriste wrangler types. El nombre lo eliges tú; el recurso al otro lado lo identifica un id o un database_id.
flowchart LR A[wrangler.jsonc declara un binding] --> B[El runtime inyecta env] B --> C[env.DB apunta al recurso] C --> D[Sin cadena de conexion ni secreto en el codigo]
El catálogo de bindings cubre toda la plataforma, y todos siguen el mismo patrón: una clave en el manifiesto, un nombre en env. Reconocer las familias te deja leer la superficie completa de un Worker sin abrir su código.
Almacenamiento
kv_namespaces, d1_databases, r2_buckets y durable_objects: cada uno declara un recurso de datos que llega por env.
Composición
services para llamar a otro Worker por RPC y queues para productores y consumidores de mensajes asíncronos.
Inteligencia
ai para Workers AI y vectorize para búsqueda vectorial: la capa de IA, también declarada como binding.
Conectividad
hyperdrive para acelerar tu Postgres existente y assets para servir archivos estáticos desde el propio Worker.
Un matiz que separa a los bindings de datos de los demás: algunos necesitan más que su declaración. Un durable_objects exige además una entrada de migrations que registre la clase, porque un Durable Object tiene ciclo de vida propio; ahí el manifiesto no solo concede acceso a un recurso, también versiona su existencia. Es la pista de que un binding no siempre apunta a algo que ya existe: a veces lo declara por primera vez.
Entornos, y jsonc frente al viejo toml
Un mismo Worker suele vivir en varias encarnaciones: producción, staging, previsualización. El manifiesto modela esto con la clave env: la configuración del nivel superior es el entorno por defecto, y cada entrada bajo env.<nombre> la especializa.
{
"name": "mi-worker",
"main": "src/index.ts",
"compatibility_date": "2026-01-15",
"vars": { "ENTORNO": "produccion" },
"env": {
"staging": {
"vars": { "ENTORNO": "staging" }
}
}
}
Hay una trampa clásica: las claves no heredables —vars, los bindings, routes— no pasan del nivel superior a un entorno con nombre. Si un entorno declara cualquier vars, debe redeclarar todas las que necesite; el manifiesto no las mezcla por ti. Esta ausencia de herencia parcial es deliberada —evita que un entorno arrastre un binding de producción sin querer—, pero sorprende a quien la descubre en caliente.
Definir un entorno en el archivo no basta: hay que seleccionarlo al actuar. wrangler dev --env staging y wrangler deploy --env staging aplican esa sección concreta, mientras que omitir el flag usa el entorno por defecto del nivel superior. Cada entorno con nombre se despliega como un Worker distinto —con su propio nombre derivado y sus propios recursos—, así que el flag no es un detalle cosmético: decide contra qué instancia real trabajas.
Históricamente este archivo era wrangler.toml. El toml sigue soportado, pero desde finales de 2024 el formato por defecto es wrangler.jsonc —JSON con comentarios—, y en 2026 es lo que genera C3 y lo que verás en la documentación. La razón es práctica: JSON lo genera y lo lee una máquina sin ambigüedad, encaja con el resto del ecosistema y expresa mejor la estructura anidada de bindings y entornos; el sufijo jsonc recupera lo único bueno que el JSON puro no tenía, los comentarios.
Dos ideas convergen en wrangler.jsonc y, juntas, explican por qué el modelo de Cloudflare se siente distinto. La primera es capacidades en vez de credenciales: cuando declaras un binding, no estás guardando un secreto, estás concediendo un derecho. El Worker no sabe dónde vive físicamente su base de datos ni tiene una llave para abrirla; recibe, ya conectado, exactamente aquello que el manifiesto autorizó, y nada más. Eso convierte el archivo en un mapa exacto de la superficie de ataque: para saber qué puede tocar un Worker no auditas su código, lees su manifiesto. La segunda idea es el anclaje temporal. compatibility_date fija el comportamiento del runtime a una fecha, de modo que el mismo código, redesplegado dentro de años, se ejecuta contra la misma semántica con la que lo escribiste; la plataforma evoluciona sin romperte porque tú decides cuándo avanzas en el tiempo. La consecuencia es que el manifiesto no describe una máquina, describe un contrato: qué recursos, con qué runtime, bajo qué nombre. Aprender a leerlo así —como la frontera declarada entre tu compute y la plataforma— es lo que te permite entender un sistema del edge sin abrir una sola línea de su lógica.
- Escribe un
wrangler.jsoncmínimo con$schema,name,mainy unacompatibility_datede hoy; confirma que el editor te autocompleta. - Añade un binding de KV llamado
CACHEy uno de D1 llamadoDB; ejecutawrangler typesy observa cómo aparecen en el tipoEnv. - Define un entorno
stagingcon unavars.ENTORNOdistinta y comprueba conwrangler deploy --env staging --dry-runque se selecciona esa sección. - Verifica que la variable de producción no se hereda al entorno
staginga menos que la repitas. - Cambia
compatibility_datea una fecha muy antigua, luego a hoy, y razona qué comportamiento del runtime podría diferir entre ambas.