How to return a plain object and not a stub from a Worker with RPC binding?

I'm working with a Cloudflare Worker that is called from a Cloudflare Pages project using Services Binding + RPC. I'd like to be able to configure or convert a plain object returned from a target instance to be serialized to an object and not be returned as a stub. As an example, consider the following projects: TYPE LIB: Standalone library with types I want to reference in other projects.
export type MyObject = {
name: string;
description: string;
}
export type MyObject = {
name: string;
description: string;
}
SHARED WORKER: Worker providing services via service bindings and RPC.
export default class extends WorkerEntrypoint {
async myStore(): Promise<myStore> {
return new MyStoreTarget();
}
);

class MyStoreTarget extends RpcTarget {
async findMyObject(name: string) : Promise<MyObject | null> { // references TYPE LIB
return { name: 'Test name', description: 'Test description' };
}
export default class extends WorkerEntrypoint {
async myStore(): Promise<myStore> {
return new MyStoreTarget();
}
);

class MyStoreTarget extends RpcTarget {
async findMyObject(name: string) : Promise<MyObject | null> { // references TYPE LIB
return { name: 'Test name', description: 'Test description' };
}
END APP: The calling code to the shared worker.
const worker = event.platform.env.STORE_WORKER; // references SHARED WORKER

const target = await worker.myStore();

const myobj : MyObject = await target.findMyObject('Test name'); // references TYPE LIB
const worker = event.platform.env.STORE_WORKER; // references SHARED WORKER

const target = await worker.myStore();

const myobj : MyObject = await target.findMyObject('Test name'); // references TYPE LIB
When I try the above, it appears to return myobj as a stub, and TypeScript in VSCode complains as the types don't match. Further, it would seem any reference to myobj properties would be executed as RPC calls which is inefficient. Is there a way to configure or convert myobj efficiently so that it is a plain object matching the MyObject type? Thanks in advance!
1 Reply
Blazius
Blazius2d ago
Bumping this as I found myself in a very similar situation! If we change OP's example MyObject to:
type MyObject = {
name: string;
birthDate: Date;
}
type MyObject = {
name: string;
birthDate: Date;
}
then the returned object has the following type:
{
name: string;
birthDate: {
toString: Rpc.Stub<() => string>;
toDateString: Rpc.Stub<() => string>;
toTimeString: Rpc.Stub<() => string>;
toLocaleString: Rpc.Stub<...>;
... 39 more ...;
[Symbol.toPrimitive]: Rpc.Stub<...>;
}
};
{
name: string;
birthDate: {
toString: Rpc.Stub<() => string>;
toDateString: Rpc.Stub<() => string>;
toTimeString: Rpc.Stub<() => string>;
toLocaleString: Rpc.Stub<...>;
... 39 more ...;
[Symbol.toPrimitive]: Rpc.Stub<...>;
}
};
This is of course just an example. My use case is with a large and complex zod parsed object... array. Now, either I don't fully understand when and how RPC methods via service-bound Workers should be used, or there are issues in @cloudflare/workers-types: 4.20241205.0 which defines:
type UnstubifyAll<A extends any[]> = {
[I in keyof A]: Unstubify<A[I]>;
};
type UnstubifyAll<A extends any[]> = {
[I in keyof A]: Unstubify<A[I]>;
};
but might not be called/applied. TLDR: Are we always supposed to serialize and deserialize complex objects between Workers? Then what's the purpose of RPC methods "running on the same thread" if we can't pass concrete typed objects?
Want results from more Discord servers?
Add your server