Qué es un service binding
El binding que no apunta a un almacén sino a otro Worker. En lugar de publicar tu segundo Worker con una URL y llamarlo por HTTP, lo declaras como servicio y lo invocas directamente: la llamada no sale jamás a la red pública, no paga el peaje de DNS, TLS ni un viaje de ida y vuelta por internet, y compone dos Workers como piezas de un solo sistema.
Tarde o temprano tu aplicación deja de ser un solo Worker. Aparece un segundo —uno valida sesiones, otro redimensiona imágenes— y surge la pregunta inevitable: ¿cómo hace el primero para hablar con el segundo? El reflejo heredado de los microservicios es publicar el segundo con una URL y llamarlo por HTTP. Cloudflare ofrece otra vía, más íntima y mucho más barata: un service binding, que enchufa un Worker a otro directamente, sin que la llamada llegue a salir a la internet pública.
- Definir un service binding como una capacidad hacia otro Worker, no hacia un almacén.
- Contrastar la llamada por URL pública con la invocación directa por binding.
- Explicar por qué la llamada no paga DNS, TLS ni un viaje por internet.
- Reconocer el binding como un cableado fijado al desplegar, no en tiempo de ejecución.
El problema: dos Workers que se necesitan
A medida que una aplicación crece, dejas de meterlo todo en un Worker. El que lo hacía todo se parte en un Worker para la API pública y otro que solo verifica identidad. Y en cuanto existen dos, el primero necesita hablar con el segundo. El instinto que traes del mundo de los microservicios es darle al Worker de autenticación una URL pública y llamarla por HTTP desde el otro.
Funciona, pero fíjate en lo que obligas a hacer a esa llamada. Una petición entre dos piezas que son tuyas, que viven en la misma red y a menudo en la misma máquina, se ve forzada a recorrer el mismo camino que la de un desconocido cualquiera de internet: resolver un nombre por DNS, abrir un canal TLS con su saludo criptográfico, cruzar la red pública, autenticarse contra la API y volver. Pagas el peaje completo de la red pública por una conversación doméstica.
Un binding que apunta a cómputo
Un service binding es, como todo binding, una capacidad declarada que aparece en env. Pero donde un binding de KV o de D1 apunta a un almacén, un service binding apunta a otro Worker. Lo declaras en wrangler.jsonc:
{
"name": "api",
"services": [
{ "binding": "AUTH", "service": "auth-worker" }
]
}
A partir de ahí, env.AUTH es una referencia viva al otro Worker. La forma más simple de usarlo calca el modelo de petición y respuesta que ya conoces: le entregas un Request y te devuelve un Response, igual que un fetch, salvo que el destino es un Worker cableado por nombre y no una URL.
export default {
async fetch(request, env, ctx): Promise<Response> {
// env.AUTH es otro Worker, invocado sin URL ni credenciales
const veredicto = await env.AUTH.fetch(request);
if (veredicto.status !== 200) {
return new Response("no autorizado", { status: 401 });
}
return new Response("recurso protegido");
},
} satisfies ExportedHandler<Env>;
Fíjate en lo que no aparece: ninguna URL. No escribiste la dirección de auth-worker; nombraste una capacidad, AUTH, y la plataforma la resolvió a un Worker concreto. A cuál apunta se decide en la configuración, no en el código —la misma indirección que viste en todos los demás bindings—.
La llamada que nunca sale a internet
Aquí está el corazón del asunto. Cuando llamas por una URL pública, la petición sale del edificio de Cloudflare, resuelve DNS, negocia TLS, viaja por internet y regresa —aunque el Worker de destino viva en el mismísimo centro de datos que el que llama—. Un service binding colapsa ese recorrido: la invocación se queda dentro de la infraestructura de Cloudflare, sin DNS, sin apretón de manos TLS, sin viaje de ida y vuelta por la red pública. Los dos Workers se componen como funciones de un mismo programa, no como servicios remotos que se buscan a ciegas.
Conviene ser honesto con la expresión latencia cero: no es que el tiempo desaparezca por arte de magia, sino que el sumando que domina en una petición corta —el saludo TLS y el ida y vuelta por la red— se reduce a prácticamente nada porque los dos Workers están coubicados. Lo que pagabas antes no era el cómputo del otro Worker, sino el trayecto para llegar a él; y ese trayecto es justo lo que el binding elimina.
Sin DNS ni TLS
No se resuelve ningún nombre ni se negocia ningún canal cifrado: la llamada no toca la pila de red pública, así que se esfuma el saludo criptográfico que domina la latencia de una petición corta.
Sin peaje de subrequest externo
No hay tránsito por internet ni egress que facturar por el salto. El Worker invocado se ejecuta y se cuenta, pero el viaje entre ambos no añade el coste de una petición externa.
Cableado al desplegar
El vínculo entre los dos Workers se fija cuando despliegas, no cuando corre el código. Leer la lista de services es leer, exactamente, con qué otros Workers habla este.
Invocar con env.AUTH.fetch(request) es la forma más antigua y sencilla, y aún es útil cuando quieres reenviar una petición entera. Pero es una conversación de bajo nivel: empaquetas todo en un Request y desempaquetas un Response. En la próxima lección verás la vía moderna —RPC con WorkerEntrypoint—, que te deja llamar métodos tipados del otro Worker como si fueran locales, sin la ceremonia de construir peticiones a mano.
flowchart LR A1[Worker A por URL] --> DNS[resolver DNS] --> TLS[saludo TLS] --> NET[red publica] --> B1[Worker B] A2[Worker A por binding] --> BND[binding AUTH] --> B2[Worker B] style NET fill:#f9e2af,color:#11111b style BND fill:#a6e3a1,color:#11111b
Durante veinte años, partir una aplicación en servicios separados cargó siempre con un impuesto cruel: ganabas modularidad —piezas que se despliegan y escalan solas— pero pagabas el precio de la red entre ellas. Cada llamada interna se volvía una petición HTTP con su latencia, su saludo TLS, su serialización, su autenticación y su nuevo abanico de modos de fallo. La distribución te cobraba por adelantado, y ese peaje era tan real que muchos equipos mantenían monolitos solo para no pagarlo. El service binding rompe ese trato. Al mantener la llamada dentro de la frontera de confianza de la plataforma, te deja separar dos Workers en unidades desplegables independientes sin pagar casi nada por hablar entre ellas: la composición ocurre en el recinto, no en la intemperie. Y hay un segundo regalo, más silencioso. Como el vínculo se declara en la configuración y se cablea al desplegar, la topología de tu sistema se vuelve legible: la lista de services de cada Worker es un mapa exacto de con quién habla, sin llamadas ocultas tras una URL escondida en el código. Interiorizar esto cambia la economía de tus decisiones de arquitectura. La pregunta deja de ser si puedes permitirte el coste de red de separar esta pieza y pasa a ser si esta pieza merece ser su propia unidad de despliegue. El impuesto mecánico de descomponer —la red, la latencia, las credenciales— casi desaparece; lo que queda es la única pregunta que siempre importó de verdad, la de dónde trazar las fronteras.
- Crea dos Workers: uno llamado
apiy otroauth-workerque responda200solo si la cabeceraauthorizationtrae un valor concreto. - Declara en
apiun service bindingAUTHhaciaauth-workery reenvíale la petición conenv.AUTH.fetch(request). - Despliega ambos y comprueba que
apirechaza con401cuando falta la cabecera y responde cuando está presente. - Abre las dos
wrangler.jsoncy dibuja el grafo de llamadas leyendo solo la secciónservices: convéncete de que la topología vive en la config, no en el código. - Razona en una frase por qué esta llamada es más barata que publicar
auth-workercon una URL y llamarla por HTTP.