shiichan

Just a Few Lines of Code Connects Python and JavaScript Workers Directly!

Hey, it's Shii-chan again! Today's news from the Cloudflare Changelog is the kind that makes developers grin.

Cloudflare Changelog developers.cloudflare.com

What was announced?

According to the Cloudflare Changelog, Workers RPC now works between Python Workers and JavaScript Workers too. It's built on Service bindings, so you don't need any extra dependencies, schema definitions, or serialization code. Calls behave like ordinary function calls, and exceptions propagate all the way back to the caller.

The story so far

Workers RPC used to only work between Workers written in the same language. If you wanted a Python Worker and a JavaScript Worker to talk to each other, you had to call over HTTP with fetch or design your own serialization format, which added real friction.

What changes

Now, as long as you set up a Service binding, a Python Worker can call a method on a JavaScript Worker directly, and vice versa. Parameters and return values can be any structured cloneable type, and Pyodide's Foreign Function Interface (FFI) automatically handles type conversion between the two languages.

Dive Deep

Here's what calling a TypeScript Worker from Python looks like. First, define a method in the TypeScript Worker using WorkerEntrypoint:

import { WorkerEntrypoint } from "cloudflare:workers";

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

Call it from a Python Worker through the Service binding:

from workers import Response, WorkerEntrypoint

class Default(WorkerEntrypoint):
	async def fetch(self, request):
		rpc = self.env.RPC
		result = await rpc.add(42, 144)
		return Response.json({"result": result})

The Python Worker's Wrangler configuration looks like this:

[[services]]
binding = "RPC"
service = "ts-rpc-server"
entrypoint = "RpcService"

It works the other way too. Say a Python Worker exposes a syntax-highlighting method built on Pygments:

from workers import WorkerEntrypoint

class Default(WorkerEntrypoint):
	async def highlight_code(self, code: str, language: str) -> dict:
		from pygments.formatters import HtmlFormatter
		from pygments import highlight
		from pygments.lexers import get_lexer_by_name

		lexer = get_lexer_by_name(language, stripall=True)
		formatter = HtmlFormatter(linenos=True, cssclass="highlight", style="monokai")
		highlighted_html = highlight(code, lexer, formatter)
		css = formatter.get_style_defs(".highlight")

		return {"html": highlighted_html, "css": css}

A JavaScript Worker can call it directly:

export default {
	async fetch(request, env) {
		const rpc = env.PYTHON_RPC;
		const result = await rpc.highlight_code("print(42)", "python");
		return Response.json(result);
	},
};

And the JavaScript Worker's Wrangler configuration is just this:

[[services]]
binding = "PYTHON_RPC"
service = "py-rpc-server"

Wrap-up

  • Workers RPC now works between Python Workers and JavaScript Workers
  • A Service binding alone lets you call methods directly across languages, with no APIs, schemas, or serialization code
  • Structured cloneable types are supported, and Pyodide's FFI handles type conversion automatically
  • Exceptions propagate straight back to the caller

If you want to reuse a Python library from JavaScript, or call into the JS ecosystem from a Python Worker, this is a copy-paste-and-try update.