Use RpcTarget class to handle Durable Object metadata

Pass metadata to a Durable Object using RpcTarget.

Last updated Oct 18, 2026 When working with Durable Objects, you will often need the name that was used to create the Durable Object via idFromName(). This name is typically a meaningful identifier that represents what the Durable Object is responsible for (like a user ID, room name, or resource identifier).

Note

If the caller accesses the Durable Object with getByName() or idFromName(), you can read the name directly inside the Durable Object from this.ctx.id.name. You do not need the pattern on this page for that. Refer to Durable Object ID for details, including when ctx.id.name is undefined.

The RpcTarget pattern in the following examples is useful when you need to pass metadata that is not available on ctx.id, or when ctx.id.name is undefined (for example, when the caller uses idFromString()). It creates a communication layer that automatically carries the metadata with each method call, which keeps your API clean.

Based on your needs, you can either store the metadata temporarily in the RpcTarget class, or use Durable Object storage to persist the metadata for the lifetime of the object.

This example does not persist the Durable Object metadata. It demonstrates how to:

  1. Create an RpcTarget class
  2. Set the Durable Object metadata (identifier in this example) in the RpcTarget class
  3. Pass the metadata to a Durable Object method
  4. Clean up the RpcTarget class after use
import { DurableObject, RpcTarget } from "cloudflare:workers";

//  * Create an RpcDO class that extends RpcTarget
//  * Use this class to set the Durable Object metadata
//  * Pass the metadata in the Durable Object methods
//  * @param mainDo - The main Durable Object class
//  * @param doIdentifier - The identifier of the Durable Object

export class RpcDO extends RpcTarget {
    constructor(
        private mainDo: MyDurableObject,
        private doIdentifier: string,
    ) {
        super();
    }

    //  * Pass the user's name to the Durable Object method
    //  * @param userName - The user's name to pass to the Durable Object method

    async computeMessage(userName: string): Promise<string> {
        // Call the Durable Object method and pass the user's name and the Durable Object identifier
        return this.mainDo.computeMessage(userName, this.doIdentifier);
    }

    //  * Call the Durable Object method without using the Durable Object identifier
    //  * @param userName - The user's name to pass to the Durable Object method

    async simpleGreeting(userName: string) {
        return this.mainDo.simpleGreeting(userName);
    }
}

//  * Create a Durable Object class
//  * You can use the RpcDO class to set the Durable Object metadata

export class MyDurableObject extends DurableObject<Env> {
    constructor(ctx: DurableObjectState, env: Env) {
        super(ctx, env);
    }

    //  * Initialize the RpcDO class
    //  * You can set the Durable Object metadata here
    //  * It returns an instance of the RpcDO class
    //  * @param doIdentifier - The identifier of the Durable Object

    async setMetaData(doIdentifier: string) {
        return new RpcDO(this, doIdentifier);
    }

    //  * Function that computes a greeting message using the user's name and DO identifier
    //  * @param userName - The user's name to include in the greeting
    //  * @param doIdentifier - The identifier of the Durable Object

    async computeMessage(
        userName: string,
        doIdentifier: string,
    ): Promise<string> {
        console.log({
            userName: userName,
            durableObjectIdentifier: doIdentifier,
        });
        return `Hello, ${userName}! The identifier of this DO is ${doIdentifier}`;
    }

    //  * Function that is not in the RpcTarget
    //  * Not every function has to be in the RpcTarget

    private async notInRpcTarget() {
        return "This is not in the RpcTarget";
    }

    //  * Function that takes the user's name and does not use the Durable Object identifier
    //  * @param userName - The user's name to include in the greeting

    async simpleGreeting(userName: string) {
        // Call the private function that is not in the RpcTarget
        console.log(this.notInRpcTarget());

        return `Hello, ${userName}! This doesn't use the DO identifier.`;
    }
}

export default {
    async fetch(request, env, ctx): Promise<Response> {
        let id: DurableObjectId = env.MY_DURABLE_OBJECT.idFromName(
            new URL(request.url).pathname,
        );
        let stub = env.MY_DURABLE_OBJECT.get(id);

        //  * Set the Durable Object metadata using the RpcTarget
        //  * Notice that no await is needed here

        const rpcTarget = stub.setMetaData(id.name ?? "default");

        // Call the Durable Object method using the RpcTarget.
        // The DO identifier is passed in the RpcTarget
        const greeting = await rpcTarget.computeMessage("world");

        // Call the Durable Object method that does not use the Durable Object identifier
        const simpleGreeting = await rpcTarget.simpleGreeting("world");

        // Clean up the RpcTarget.
        try {
            (await rpcTarget)[Symbol.dispose]?.();
            console.log("RpcTarget cleaned up.");
        } catch (e) {
            console.error({
                message: "RpcTarget could not be cleaned up.",
                error: String(e),
                errorProperties: e,
            });
        }

        return new Response(greeting, { status: 200 });
    },
} satisfies ExportedHandler<Env>;

This example persists the Durable Object metadata. It demonstrates similar steps as the previous example, but uses Durable Object storage to store the identifier, eliminating the need to pass it through the RpcTarget.

import { DurableObject, RpcTarget } from "cloudflare:workers";

//  * Create an RpcDO class that extends RpcTarget
//  * Use this class to set the Durable Object metadata
//  * Pass the metadata in the Durable Object methods
//  * @param mainDo - The main Durable Object class
//  * @param doIdentifier - The identifier of the Durable Object

export class RpcDO extends RpcTarget {
    constructor(
        private mainDo: MyDurableObject,
        private doIdentifier: string,
    ) {
        super();
    }

    //  * Pass the user's name to the Durable Object method
    //  * @param userName - The user's name to pass to the Durable Object method

    async computeMessage(userName: string): Promise<string> {
        // Call the Durable Object method and pass the user's name and the Durable Object identifier
        return this.mainDo.computeMessage(userName, this.doIdentifier);
    }

    //  * Call the Durable Object method without using the Durable Object identifier
    //  * @param userName - The user's name to pass to the Durable Object method

    async simpleGreeting(userName: string) {
        return this.mainDo.simpleGreeting(userName);
    }
}

//  * Create a Durable Object class
//  * You can use the RpcDO class to set the Durable Object metadata

export class MyDurableObject extends DurableObject<Env> {
    constructor(ctx: DurableObjectState, env: Env) {
        super(ctx, env);
    }

    //  * Initialize the RpcDO class
    //  * You can set the Durable Object metadata here
    //  * It returns an instance of the RpcDO class
    //  * @param doIdentifier - The identifier of the Durable Object

    async setMetaData(doIdentifier: string) {
        // Use DO storage to store the Durable Object identifier
        await this.ctx.storage.put("doIdentifier", doIdentifier);
        return new RpcDO(this, doIdentifier);
    }

    //  * Function that computes a greeting message using the user's name and DO identifier
    //  * @param userName - The user's name to include in the greeting

    async computeMessage(userName: string): Promise<string> {
        // Get the DO identifier from storage
        const doIdentifier = await this.ctx.storage.get("doIdentifier");
        console.log({
            userName: userName,
            durableObjectIdentifier: doIdentifier,
        });
        return `Hello, ${userName}! The identifier of this DO is ${doIdentifier}`;
    }

    //  * Function that is not in the RpcTarget
    //  * Not every function has to be in the RpcTarget

    private async notInRpcTarget() {
        return "This is not in the RpcTarget";
    }

    //  * Function that takes the user's name and does not use the Durable Object identifier
    //  * @param userName - The user's name to include in the greeting

    async simpleGreeting(userName: string) {
        // Call the private function that is not in the RpcTarget
        console.log(this.notInRpcTarget());

        return `Hello, ${userName}! This doesn't use the DO identifier.`;
    }
}

export default {
    async fetch(request, env, ctx): Promise<Response> {
        let id: DurableObjectId = env.MY_DURABLE_OBJECT.idFromName(
            new URL(request.url).pathname,
        );
        let stub = env.MY_DURABLE_OBJECT.get(id);

        //  * Set the Durable Object metadata using the RpcTarget
        //  * Notice that no await is needed here

        const rpcTarget = stub.setMetaData(id.name ?? "default");

        // Call the Durable Object method using the RpcTarget.
        // The DO identifier is stored in the Durable Object's storage
        const greeting = await rpcTarget.computeMessage("world");

        // Call the Durable Object method that does not use the Durable Object identifier
        const simpleGreeting = await rpcTarget.simpleGreeting("world");

        // Clean up the RpcTarget.
        try {
            (await rpcTarget)[Symbol.dispose]?.();
            console.log("RpcTarget cleaned up.");
        } catch (e) {
            console.error({
                message: "RpcTarget could not be cleaned up.",
                error: String(e),
                errorProperties: e,
            });
        }

        return new Response(greeting, { status: 200 });
    },
} satisfies ExportedHandler<Env>;

Was this helpful?

YesNo

On this page

Use RpcTarget class to handle Durable Ob - 图1Docs

{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/durable-objects/examples/reference-do-name-using-init/#page","headline":"Use RpcTarget class to handle Durable Object metadata","description":"Pass metadata to a Durable Object using RpcTarget.","url":"https://developers.cloudflare.com/durable-objects/examples/reference-do-name-using-init/","inLanguage":"en","image":"https://developers.cloudflare.com/durable-objects/examples/reference-do-name-using-init/og.png?v=7abae1644e934869","dateModified":"2026-10-18","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}


description: Write tests for Durable Objects using the Workers Vitest integration. title: Testing Durable Objects image: https://developers.cloudflare.com/durable-objects/examples/testing-with-durable-objects/og.png?v=1e1b96c330a686da


Skip to content

Documentation Index
Fetch the complete documentation index at: https://developers.cloudflare.com/durable-objects/llms.txt
Use this file to discover all available pages before exploring further.