wandres.dev
SERVICE BINDINGS · worker a worker

Service binding frente a fetch externo

Dos formas de comunicar Workers que se parecen en el código pero difieren en naturaleza: llamar por una URL pública o cablear un service binding. El análisis por los tres ejes en que divergen —velocidad, seguridad y coste— y la pregunta honesta de cuándo el HTTP público sigue siendo la elección correcta.

⏱ 14 min

Puedes lograr que un Worker hable con otro de dos maneras que en el editor se parecen sospechosamente: darle al segundo una ruta pública y hacer fetch a su URL, o cablear un service binding e invocarlo por env. Ambas devuelven una respuesta; ambas caben en una línea. Pero no son variantes de lo mismo: difieren en naturaleza. Esta lección disecciona los tres ejes donde se separan —velocidad, seguridad y coste— y termina con la pregunta que todo ingeniero maduro se hace: ¿cuándo, pese a todo, el HTTP público sigue siendo lo correcto?

🎯 Al terminar esta lección sabrás
  • Comparar el camino de red de una llamada por URL y una por service binding.
  • Contrastar la superficie de ataque de un endpoint público con la de un binding.
  • Entender por qué el binding evita el coste de una petición externa.
  • Discernir cuándo el fetch a una URL pública sigue siendo la elección correcta.

Más rápido: la red pública desaparece

Una llamada por URL, aunque el destino esté en el mismo centro de datos, se ve obligada a comportarse como si estuviera al otro lado del planeta. Resuelve un nombre por DNS. Abre una conexión y negocia TLS, con su ida y vuelta criptográfica. Cruza la pila de red pública. Y todo eso antes de que el otro Worker ejecute una sola línea. En una petición corta, ese preámbulo domina el tiempo total: el cómputo útil es un parpadeo comparado con el ritual de llegar hasta él.

Un service binding borra el preámbulo entero. La invocación no resuelve nombres ni negocia cifrado ni sale a la red: viaja por la infraestructura interna de Cloudflare, entre Workers coubicados. Lo que quedaba —el cómputo del Worker invocado— es lo único que pagas en tiempo, porque el trayecto para llegar a él se ha reducido a prácticamente nada.

Más seguro: no hay puerta pública que defender

Una URL pública es, por definición, alcanzable por todo el mundo. Eso te obliga a tratar a cada llamante como un potencial hostil: autenticar cada petición interna con un token, poner límites de tasa, vigilar que nadie descubra el endpoint y lo martillee. Has abierto una puerta a la calle solo para que dos habitaciones de tu propia casa se hablen, y ahora tienes que ponerle cerradura y guardia.

Un service binding no es direccionable desde fuera. El Worker invocado puede no tener ninguna ruta pública en absoluto: existe solo para ser llamado por los Workers que lo declaran. Eso es mínima autoridad aplicada a la topología: solo quien tiene el binding puede invocarlo, y ese permiso se otorga explícitamente en la configuración, no se defiende a golpe de token en cada petición.

Velocidad

Sin DNS, sin TLS, sin viaje por internet. Desaparece el preámbulo que domina la latencia de una petición corta; queda solo el cómputo del Worker invocado.

🔒

Seguridad

El servicio no expone puerta pública. No hay endpoint que descubrir ni token interno que rotar: el permiso de llamar es el propio binding, otorgado en la config.

💸

Coste

Sin egress ni tránsito por la red pública por el salto. Evitas el precio de una segunda petición externa completa con su terminación TLS.

Más barato: sin peaje de la red

El coste sigue a la física. Una llamada por URL es una petición externa de pleno derecho: consume ancho de banda, paga la terminación TLS, y en muchas arquitecturas cuenta como una salida a internet que se factura. Un service binding no cruza la red pública, así que ese peaje no se cobra. Conviene la honestidad: el Worker invocado se ejecuta y su cómputo se cuenta —no es gratis hacer trabajo—, pero el salto entre ambos no añade el coste de una petición externa. Pagas el trabajo, no el trayecto.

Cuándo el HTTP público sigue teniendo sentido

Nada de esto convierte al fetch externo en un error. Hay casos en que es exactamente lo que quieres. Si el destino no es tuyo —una API de terceros, un servicio en otra nube— no hay binding posible: hablas HTTP porque hablas con un extraño. Si el destino debe ser genuinamente público —un webhook, una API abierta a clientes que no controlas— su naturaleza es una URL. Y si necesitas el desacople total de dos sistemas que se despliegan, versionan y hasta se apagan sin coordinación alguna, la frontera dura de HTTP es una virtud, no un defecto.

💡
La regla práctica

HTTP hacia fuera, binding hacia dentro. En la frontera de tu sistema, de cara al mundo que no controlas, habla HTTP con toda su ceremonia de desconfianza. Entre piezas que son tuyas y viven en Cloudflare, usa un binding y ahórrate el peaje. Confundir los dos planos —usar HTTP público para hablar contigo mismo— es pagar el impuesto de la desconfianza entre componentes que en realidad se fían.

flowchart TD
Q[A necesita llamar a B] --> D[B es tuyo y vive en Cloudflare]
D -->|si| BND[service binding]
D -->|no es tuyo o debe ser publico| HTTP[fetch a URL publica]
BND --> R1[sin red sin token barato]
HTTP --> R2[con red con auth facturable]
style BND fill:#a6e3a1,color:#11111b
style HTTP fill:#f9e2af,color:#11111b
Elegir el transporte es elegir dónde trazas la confianza

Debajo de la comparación técnica hay una decisión que no es técnica sino arquitectónica: dónde pones la frontera de confianza de tu sistema. HTTP es el disolvente universal de la informática —permite que cualquier cosa hable con cualquier otra, sin importar el lenguaje, la nube o el dueño— y esa universalidad es justo lo que lo hace caro. Precisamente porque un endpoint HTTP puede ser llamado por cualquiera, estás obligado a tratar a todo llamante como sospechoso: autenticar, limitar, cifrar, pagar la red en cada intercambio. La universalidad se paga con desconfianza, y la desconfianza se paga con latencia, ceremonia y dinero. Un service binding hace la elección opuesta: estrecha la apertura hasta un puñado de Workers nombrados y les concede hablar como parientes de confianza. Cambias universalidad por intimidad, y a cambio el impuesto de la red y el ritual de la autenticación se evaporan. La lección trasciende a Cloudflare y es de las que reordenan cómo diseñas: no todo debe hablar HTTP. HTTP pertenece a los bordes de tu sistema, donde te encuentras con lo desconocido y la desconfianza está justificada; dentro, entre piezas que ya se fían unas de otras, imponer el protocolo de los extraños es un error de categoría que muchos cometen por pura costumbre. Diseñar bien un sistema distribuido es, en buena parte, saber dibujar ese anillo: qué queda dentro del recinto de confianza, hablándose barato y directo, y qué queda fuera, obligado a identificarse en la puerta. El transporte que eliges para cada llamada no es un detalle de implementación: es la declaración, hecha código, de a quién consideras tu igual y a quién consideras un forastero.

⚔️ Mide y decide
  1. Publica un Worker con una ruta pública y llámalo desde otro por su URL con fetch; mide la latencia de la llamada extremo a extremo.
  2. Cablea el mismo Worker como service binding e invócalo; compara la latencia con la medición anterior y atribuye la diferencia al preámbulo de red.
  3. Retira la ruta pública del Worker invocado y confirma que el binding sigue funcionando: has eliminado la superficie pública sin romper la composición.
  4. Enumera, para tu propia aplicación, qué llamadas cruzan hacia terceros o deben ser públicas y cuáles son domésticas; asigna a cada una HTTP o binding.
  5. Formula en una frase la regla que usarás para decidir el transporte, en términos de dónde trazas la frontera de confianza.