## Cross-Language RPC is Now Live in Cloudflare Workers
Two years ago, we introduced Workers RPC, built on Cap’n Proto RPC. This made it possible for Workers to call other Workers and Durable Objects’ methods, return live objects and call their methods, return functions, streams, and get all the benefits of a Remote Procedure Call (RPC) system, without defining schemas or adding any dependencies. We called it “JavaScript-native RPC” because it made using RPC feel native to the language.
Last year, we made this work between web browsers and servers and introduced Cap’n Web.
Now we’re taking it cross-language.
Normally, getting programs written in different languages to talk to each other is complicated: developers usually have to build custom APIs or adopt language-agnostic serialization formats like protobuf, so the two systems can understand each other. The RPC system built into Workers is able to translate across JavaScript and Python without any additional work.
You can now call methods defined in a Python Worker from a JavaScript Worker and vice versa. You can share objects across Python and JavaScript, and call methods on a Python object from TypeScript. It all just works.
If you define a method `add()` in a Worker written in TypeScript:
“`ts
import { WorkerEntrypoint } from “cloudflare:workers”
export class RpcService extends WorkerEntrypoint {
async add(a: number, b: number): Promise
return a + b
}
}
“`
…you can simply call it from Python:
“`python
from workers import Response, WorkerEntrypoint
class Default(WorkerEntrypoint):
async def fetch(self, request):
# Get the RPC stub from the TypeScript Worker.
rpc = self.env.RPC
# Call the TypeScript RPC method.
result = await rpc.add(42, 144)
return Response.json({“result”: result})
“`
There are no dependencies needed. All you need to configure is a Service binding:
“`json
“services”: [
{
“binding”: “RPC”,
“service”: “ts-rpc-server”,
“entrypoint”: “RpcService”
}
]
“`
### So, what can you do with it?
This RPC system allows you to build a complex multi-language system as if you are using a library. Here are some features of cross-language RPC:
– Cross-language RPC calls behave like ordinary function calls that return promises in JavaScript/TypeScript and futures in Python. Exceptions are propagated and are thrown at the call site of the RPC method.
– You can pass any Structured Cloneable types as the parameters or a return value of an RPC call. These get converted to the appropriate types in Python: for example, a JS Date is converted to a Python datetime.
– You can pass JavaScript functions to a Python Worker and return them, and vice versa. When the other side calls the function passed to it, they make a new RPC back for you.
– Typically, RPC to another Worker does not cross a network. The other Worker usually runs in the same thread as the caller. There is near-zero performance overhead compared to running code in the same Worker.
– The implementation is fully open source as part of `workerd` and `workers-runtime-sdk`.
#### But wait, how do you convert types across languages?
The main hurdle for making RPC seamless across the JavaScript and Python Workers is bridging their distinct type systems. Our goal was to make cross-language RPC completely transparent. Developers should feel like they are writing code for a single-language application without needing to worry about the underlying translation layer. We achieved this by combining Pyodide’s Foreign Function Interface (FFI) with a custom type-conversion layer for Python Workers.
Pyodide FFI already translates between Python and JavaScript types. When a Python Worker communicates with a JavaScript Worker via Service bindings, Pyodide’s FFI transparently converts objects during the RPC call.
Pyodide maps native types between both environments out of the box:
| Python Type | JavaScript Equivalent |
|————-|———————-|
| int, float | Number |
| bool | Boolean |
| dict | Object |
| list | Array |
When direct translation isn’t possible (such as with custom classes or functions), Pyodide creates a `Proxy` object.
#### Handling Cloudflare Workers objects
While Pyodide FFI seamlessly converts standard built-in types, it doesn’t automatically understand Web API objects such as `Request`, `Response`, `Blob`, or `File`. To fix this, we introduced the `workers-runtime-sdk Python package`. This acts as a thin conversion layer built specifically to handle custom Workers types over RPC. This package is included by default when you deploy a Python Worker using `uv run pywrangler deploy`.
### Use Python packages from your JavaScript Worker
Have you ever wanted to use a great Python package, but your app is written in JavaScript? You can do this with Python Workers. Let’s look at an example using Pygments, a popular syntax highlighting package written in Python.
We can call this method in our JavaScript by accessing the request’s `env`:
“`ts
export default {
async fetch(request, env) {
// Get the RPC stub from the Python Worker.
const rpc = env.PYTHON_RPC;
// Call the Python RPC method.
const result = await rpc.highlight_code(‘print(42)’, ‘python’);
return Response.json(result);
}
}
“`
On the Python side, we define a method like so:
“`python
from workers import WorkerEntrypoint
class Default(WorkerEntrypoint):
async def highlight_code(self, code: str, language: str) -> dict:
# Implementation goes here
“`
A full example is available on GitHub. You can run it directly with:
“`bash
git clone git@github.com:cloudflare/python-workers-examples.git
cd python-workers-examples/13-js-api-pygments/
# Terminal 1
cd ts/
npx wrangler dev
# Terminal 2
cd py/
uv run pywrangler dev
“`
### Try it now
In addition to those above, there are far more examples and information about RPC in our documentation.
—
**Conclusion:** Cross-language RPC in Cloudflare Workers eliminates the complexity of inter-language communication, allowing developers to build truly polyglot systems with seamless type conversion, near-zero performance overhead, and a familiar developer experience. By combining Workers RPC with Pyodide FFI and the workers-runtime-sdk, JavaScript and Python code can now interoperate as if they were written in a single language. This opens up new possibilities for leveraging existing Python libraries and tools within your Cloudflare Workers projects without sacrificing performance or developer productivity.



