Resumen

Cuando un proyecto funciona perfectamente en local pero falla en producción, el problema suele estar en detalles de infraestructura que no se manifiestan en el entorno de desarrollo. En el ecosistema de Cloudflare Workers, uno de esos detalles críticos es cómo se comunican los workers entre sí, y entenderlo puede ahorrarte horas de depuración.

¿Por qué funciona en local pero no en producción?

Al ejecutar tanto el frontend como el backend en local, todo opera sin inconvenientes: la landing page de KV registra emails, valida duplicados y responde correctamente. Sin embargo, al desplegar el worker en producción y acceder a su URL, aparece un error de conexión al servidor [01:10].

Este es el escenario clásico del "pero me funciona en local". La clave está en inspeccionar el código de status del error. En este caso, se trata de un error 100 % controlado, lo que indica que el problema no es aleatorio, sino estructural.

Al revisar el código fuente, el flujo es claro: se realiza un fetch hacia una URL almacenada en una variable de entorno. Si ese fetch falla, se captura el error. El problema radica en que la landing es server render, es decir, se ejecuta dentro de un worker que intenta hacer una petición HTTP hacia otro worker del mismo entorno [03:45].

¿Cómo funciona el fetch entre workers en Cloudflare?

La documentación de Cloudflare incluye un warning importante: cuando necesitas comunicar un worker con otro, el fetch estándar no funciona directamente [03:10]. Esto ocurre porque los workers corren sobre V8, el motor de JavaScript, en un entorno aislado similar a Node.js pero más reducido.

Existen dos soluciones posibles:

  • Service binding: conecta los workers directamente a través de la red interna de Cloudflare.
  • Compatibility flag: habilita una bandera que permite el fetch externo entre workers.

¿Por qué el service binding es la opción recomendada?

Aunque el compatibility flag resuelve el problema rápidamente, tiene una desventaja significativa. Lo que hace es permitir que la petición salga a Internet y vuelva a entrar al mismo edge donde ya se encuentra [04:05]. A gran escala, esto genera:

  • Latencia adicional por el viaje innecesario hacia afuera y de vuelta.
  • Problemas de firewall, ya que tráfico interno sale por la puerta externa y hay que configurar reglas para respetarlo.

El service binding, en cambio, mantiene toda la comunicación en la capa privada de la infraestructura. Es la práctica estándar cuando tienes conexiones entre servidores: siempre es preferible que se comuniquen por su red interna.

¿Cómo mejorar la observabilidad con logs estructurados?

Un aspecto fundamental para diagnosticar errores en producción es implementar logs bien estructurados. Cloudflare recomienda no usar console.error con texto plano, sino enviar un objeto JSON donde el mensaje de error se acompaña de metadatos filtrables [02:15].

Por ejemplo, si tu JWT contiene información del usuario, puedes incluir un campo como userId en el objeto del log. Esto permite desde la interfaz de observabilidad de Cloudflare:

  • Filtrar todo el tráfico generado por un usuario específico.
  • Revisar logs de debug, info, warning y error de forma granular.
  • Reconstruir la secuencia de eventos para dar seguimiento a incidentes reportados.

¿Qué pasa cuando el objeto de error queda vacío en los logs?

Al desplegar el log de error y revisar la observabilidad en modo live, el objeto impreso aparece vacío [05:30]. Esto sucede porque el error no es un objeto JSON serializable de forma directa. La solución es convertir el error primero a texto con .toString() o utilizar JSON.stringify() antes de pasarlo al objeto del log.

Es un detalle menor pero crucial: sin esta conversión, pierdes toda la información valiosa del error y quedas igual de perdido que antes de agregar el log.

Si lograste reproducir este error y encontrar más indicios sobre la causa, comparte una captura o tus hallazgos en los comentarios.