How to create a binding TS types from RPC worker?

https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/#example--build-your-first-service-binding-using-rpc Following this typescript example, wrangler types creates an interface Env with type WORKER_B: Fetcher. This leads to bad typing when I want to call WORKER_B.add(). How this can be improved?
Cloudflare Docs
Service bindings - Runtime APIs · Cloudflare Workers docs
Facilitate Worker-to-Worker communication.
5 Replies
hunt
hunt3d ago
I haven't seen a way to do that in a typegen fashion, but it can be solved by making a edit to the env object in the referencing worker https://github.com/hntrl/durable-object-rpc-repro/blob/9804eccb6468db2d0363ca86aa1f1e96e63f7c63/packages/workerB/worker-configuration.d.ts#L4 (setting the type parameter of the Fetcher to the class of whatever it is you're trying to access)
GitHub
durable-object-rpc-repro/packages/workerB/worker-configuration.d.ts...
Contribute to hntrl/durable-object-rpc-repro development by creating an account on GitHub.
herenickname
herenicknameOP3d ago
Can I call a Durable Object from a worker of another wrangler project? I want to separate DO and api-workers that call them into different projects. So in this example I want to do this: /DOs/src/index.ts:
export class MyDurableObject extends DurableObject {
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env);
}

async sayHello(): Promise<string> {
return "Hello, World!";
}
}
export class MyDurableObject extends DurableObject {
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env);
}

async sayHello(): Promise<string> {
return "Hello, World!";
}
}
/api/src/index.ts:
export default {
async fetch(request, env) {
// Every unique ID refers to an individual instance of the Durable Object class
const id = env.MY_DURABLE_OBJECT.idFromName("foo");

// A stub is a client used to invoke methods on the Durable Object
const stub = env.MY_DURABLE_OBJECT.get(id);

// Methods on the Durable Object are invoked via the stub
const rpcResponse = await stub.sayHello();

return new Response(rpcResponse);
},
} satisfies ExportedHandler<Env>;
export default {
async fetch(request, env) {
// Every unique ID refers to an individual instance of the Durable Object class
const id = env.MY_DURABLE_OBJECT.idFromName("foo");

// A stub is a client used to invoke methods on the Durable Object
const stub = env.MY_DURABLE_OBJECT.get(id);

// Methods on the Durable Object are invoked via the stub
const rpcResponse = await stub.sayHello();

return new Response(rpcResponse);
},
} satisfies ExportedHandler<Env>;
hunt
hunt3d ago
AFAIK not in the way you're describing. you still need to expose some way to access those stubs through an entrypoint on the parent worker of your objects. (i.e. you can't create a binding to a durable object that lives on another worker) I have a similar want to what you're describing, and something like this is as close as I've come to what I'm hoping for. /DOs/src/index.ts
export class MyDurableObject extends DurableObject {
RpcTarget = class extends RpcTarget {
private object: MyDurableObject

constructor(object: MyDurableObject) {
super()
this.object = object;
}

sayHello() {
// this closure will be called on the DO, so you can access all of the object's state
return `Hello world from ${this.object.ctx.id.toString()}!`
}
}

getRpcTarget() {
return new RpcTarget(this)
}
}

export default class MyEntrypoint extends WorkerEntrypoint {
async getObject(id: string) {
const objectId = env.MY_DURABLE_OBJECT.idFromName(id);
const stub = env.MY_DURABLE_OBJECT.get(objectId);
return target = stub.getRpcTarget()
}
}
export class MyDurableObject extends DurableObject {
RpcTarget = class extends RpcTarget {
private object: MyDurableObject

constructor(object: MyDurableObject) {
super()
this.object = object;
}

sayHello() {
// this closure will be called on the DO, so you can access all of the object's state
return `Hello world from ${this.object.ctx.id.toString()}!`
}
}

getRpcTarget() {
return new RpcTarget(this)
}
}

export default class MyEntrypoint extends WorkerEntrypoint {
async getObject(id: string) {
const objectId = env.MY_DURABLE_OBJECT.idFromName(id);
const stub = env.MY_DURABLE_OBJECT.get(objectId);
return target = stub.getRpcTarget()
}
}
/api/src/index.ts
export default {
async fetch(request, env) {
const target = env.MY_OTHER_WORKER.getObject("foo");
return new Response(await target.sayHello())
}
}
export default {
async fetch(request, env) {
const target = env.MY_OTHER_WORKER.getObject("foo");
return new Response(await target.sayHello())
}
}
There's some issues regarding types when using rpc in this fashion (and maybe some inefficiencies, I'm still trying to corner that myself). https://discord.com/channels/595317990191398933/1341606596186341519
herenickname
herenicknameOP3d ago
thank you!!!
hunt
hunt2d ago
ofc!

Did you find this page helpful?