Por que RPC Cross-Language importa

Sabe aquela história de fazer Python conversar com JavaScript? Historicamente era sempre a mesma dor: ou você monta uma API HTTP na mão, ou adota um formato de serialização agnóstico tipo Protocol Buffers ou gRPC. Os dois caminhos adicionam atrito — schema pra manter, codegen pra rodar, e um modelo mental que nunca parece nativo em nenhuma das linguagens.

O Cloudflare Workers já tem RPC nativo em JavaScript há uns dois anos, construído em cima do Cap'n Proto. Você chama métodos de outros Workers e Durable Objects como se fossem funções locais — sem schema, sem dependência. No ano passado isso foi estendido pro browser com o Cap'n Web.

Agora chegou o cross-language. Um Worker JavaScript pode chamar um método de um Worker Python, e um Worker Python pode chamar de volta o JavaScript. Objetos, funções, streams e exceções cruzam a fronteira. O objetivo é simples: fazer sistemas multi-linguagem parecerem um import de biblioteca.

Se você estava postergando Python na edge por causa do glue-code, isso muda o jogo. Os dados desse post vêm do blog de engenharia da Cloudflare — 근거자료.

Python and JavaScript code editors side by side illustrating cross-language RPC type bridging IT Technology Image

A versão de 30 segundos

Define um método RPC num Worker TypeScript:

import { WorkerEntrypoint } from "cloudflare:workers";

export class RpcService extends WorkerEntrypoint {
  async add(a: number, b: number): Promise<number> {
    return a + b;
  }
}

Chama do Python — sem import, sem SDK, sem schema:

from workers import Response, WorkerEntrypoint

class Default(WorkerEntrypoint):
    async def fetch(self, request):
        # Pega o stub RPC do Worker TypeScript.
        rpc = self.env.RPC
        # Chama o método RPC do TypeScript.
        result = await rpc.add(42, 144)
        return Response.json({"result": result})

Configura com um único binding em wrangler.jsonc:

"services": [
  {
    "binding": "RPC",
    "service": "ts-rpc-server",
    "entrypoint": "RpcService"
  }
]

Pronto. rpc.add(42, 144) retorna uma promise no JS e um future no Python. Exceções propagam e são lançadas no call site.

Exemplo real: Pygments direto do JavaScript

Quer usar um pacote Python num app JavaScript? Olha só como expor o Pygments (highlighter de sintaxe) como método RPC.

Lado TypeScript — chama o Worker Python:

export default {
  async fetch(request, env) {
    // Pega o stub RPC do Worker Python.
    const rpc = env.PYTHON_RPC;
    // Chama o método RPC do Python.
    const result = await rpc.highlight_code('print(42)', 'python');
    return Response.json(result);
  }
}

Lado Python — implementa o método:

from workers import WorkerEntrypoint
from pygments import highlight
from pygments.formatters import HtmlFormatter
from pygments.lexers import get_lexer_by_name

class Default(WorkerEntrypoint):
    async def highlight_code(self, code: str, language: str) -> dict:
        # Pega o lexer da linguagem informada.
        lexer = get_lexer_by_name(language, stripall=True)
        # Cria o formatter e roda o highlighter no código.
        formatter = HtmlFormatter(linenos=True, cssclass="highlight", style="monokai")
        highlighted_html = highlight(code, lexer, formatter)
        # Pega o CSS pra estilização.
        css = formatter.get_style_defs(".highlight")
        return {"html": highlighted_html, "css": css}

Amara os dois no config do Worker JS:

"services": [
  { "binding": "PYTHON_RPC", "service": "py-rpc-server" }
]

Roda os dois dev servers em terminais separados:

# Terminal 1 — Worker JS
npx wrangler dev

# Terminal 2 — Worker Python
uv run pywrangler dev

Tem exemplo completo no repo python-workers-examples da Cloudflare.

Cloudflare Workers serverless edge architecture diagram showing Python and TypeScript workers communicating via RPC

Como a ponte de tipos funciona de verdade

RPC cross-language só funciona se os sistemas de tipos concordarem. A Cloudflare usa a Foreign Function Interface (FFI) do Pyodide — a mesma camada CPython-para-WebAssembly que roda Python Workers desde o começo — mais uma camada fina de conversão pros objetos específicos do Workers.

Mapeamento de tipos nativos

Tipo PythonEquivalente JavaScript
int, floatNumber
boolBoolean
dictObject
listArray
datetimeDate

Quando não existe mapeamento direto (classes customizadas, funções), o Pyodide cria um Proxy que encaminha acesso a atributos e chamadas de método através da fronteira. É isso que permite passar uma função Python como callback pro JavaScript.

Keyword arguments funcionam direto

Um método JS tipo get(key, options?) pode ser chamado do Python dos dois jeitos:

# Estilo objeto (dicionário)
JSRPC.get("myKey", { "type": "text" })

# Keyword arguments nativos do Python
JSRPC.get("myKey", type="text")

Os dois traduzem pro formato exato que o Worker JS espera.

Lidando com objetos da Web API

O Pyodide não entende Request, Response, Blob ou File nativamente. Por padrão ele embrulha em proxies JavaScript — funcional, mas vaza detalhes. O pacote workers-runtime-sdk (incluído automaticamente quando você faz deploy com uv run pywrangler deploy) intercepta esses objetos e converte pra formas idiomáticas do Python.

from workers import Response  # já tá usando o SDK

Limitações e pegadinhas

  • Nem todo tipo faz round-trip limpo. Tipos Structured Cloneable funcionam; qualquer coisa com referência circular ou estado host-bound não.
  • Zero-copy não é garantido. Payloads grandes ainda serializam na fronteira — não assuma que passar um array de 100 MB é de graça.
  • Premissa de mesma thread. RPC entre Workers geralmente roda na mesma thread do caller, dando overhead quase nulo. Se você forçar chamada cross-network, essa vantagem some.
  • Cold start do Python Worker ainda existe. O boot do Pyodide é real; mantenha imports pesados tipo Pygments lazy se latência importar.
  • Debugar é mais difícil. Stack traces atravessam dois runtimes. Reserve tempo extra quando der ruim.

Developers deploying Python Workers with pywrangler on Cloudflare edge network dashboard Developer Related Image

Por onde continuar

Se você já roda Workers, o ganho mais rápido é pegar uma biblioteca Python que você sempre quis usar — Pygments, Pillow, pandas (dentro dos limites de tamanho) — e expor como método RPC. A config do binding são três linhas.

Pra times construindo sistemas poliglotas, isso é uma mudança real: você não precisa mais escolher uma linguagem pro serviço inteiro. Escreve o hot path em TypeScript, mantém o processamento de dados em Python, e deixa o RPC cuidar da costura.

Se você curte pensar em como controle de acesso em nível de plataforma molda workflows de time, vale ler Vercel Just Gave Pro Teams the Developer Role — What It Means for Your Workflow — a mesma tendência de granularidade tá aparecendo em toda plataforma edge.

E se você tá avaliando onde rodar cargas analíticas pesadas que ficam perto dos seus Workers, Real-Time ERP Analytics on AWS: How Oldcastle Solved Batch Reporting with Aurora and QuickSight é um case sólido pro lado de dados da mesma arquitetura.

Próximos passos: lê a doc de RPC do Cloudflare, depois clona o repo python-workers-examples. Sobe algo pequeno essa semana. 🚀

Este conteúdo foi elaborado com o auxílio de ferramentas de IA, com base em fontes confiáveis, e revisado pela nossa equipe editorial antes da publicação. Não substitui o aconselhamento de um profissional especializado.