Skip to content

Commit a797d4b

Browse files
Oxyjunhyperlint-ai[bot]harshil1712marciocloudflare
authored
[DO] Reference DO name using init (#20309)
* Adding placeholder file * Setting up file for main code example. * Adding new example chapter. * Adding temporary code snippet for workaround. * Fixing code block language * Update src/content/docs/durable-objects/examples/reference-do-name-using-init.mdx Co-authored-by: hyperlint-ai[bot] <154288675+hyperlint-ai[bot]@users.noreply.github.com> * update the code example * Quick review + removing TypeScriptExample to avoid breaking the docs. * Fixing linebreaks * remove example for D1 * Shortening title * minor updates * PCX review * Apply suggestions from code review Co-authored-by: marciocloudflare <[email protected]> * Adding minor fix to trigger GH pipeline. --------- Co-authored-by: hyperlint-ai[bot] <154288675+hyperlint-ai[bot]@users.noreply.github.com> Co-authored-by: Harshil Agrawal <[email protected]> Co-authored-by: marciocloudflare <[email protected]>
1 parent d61844a commit a797d4b

File tree

1 file changed

+268
-0
lines changed

1 file changed

+268
-0
lines changed
Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
---
2+
type: example
3+
summary: Access the name from within a Durable Object using RpcTarget.
4+
tags:
5+
- Durable Objects
6+
pcx_content_type: example
7+
title: Use RpcTarget class to handle Durable Object metadata
8+
sidebar:
9+
order: 3
10+
description: Access the name from within a Durable Object using RpcTarget.
11+
---
12+
13+
import { TabItem, Tabs, GlossaryTooltip } from "~/components";
14+
15+
When working with Durable Objects, you will need to access 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).
16+
17+
However, there is a limitation in the current implementation: even though you can create a Durable Object with `.idFromName(name)`, you cannot directly access this name inside the Durable Object via `this.ctx.id.name`.
18+
19+
The `RpcTarget` pattern shown below offers a solution by creating a communication layer that automatically carries the name with each method call. This keeps your API clean while ensuring the Durable Object has access to its own name.
20+
21+
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.
22+
23+
This example does not persist the Durable Object metadata. It demonstrates how to:
24+
25+
1. Create an `RpcTarget` class
26+
2. Set the Durable Object metadata (identifier in this example) in the `RpcTarget` class
27+
3. Pass the metadata to a Durable Object method
28+
4. Clean up the `RpcTarget` class after use
29+
30+
```ts
31+
import { DurableObject, RpcTarget } from "cloudflare:workers";
32+
33+
// * Create an RpcDO class that extends RpcTarget
34+
// * Use this class to set the Durable Object metadata
35+
// * Pass the metadata in the Durable Object methods
36+
// * @param mainDo - The main Durable Object class
37+
// * @param doIdentifier - The identifier of the Durable Object
38+
39+
export class RpcDO extends RpcTarget {
40+
constructor(
41+
private mainDo: MyDurableObject,
42+
private doIdentifier: string,
43+
) {
44+
super();
45+
}
46+
47+
// * Pass the user's name to the Durable Object method
48+
// * @param userName - The user's name to pass to the Durable Object method
49+
50+
async computeMessage(userName: string): Promise<string> {
51+
// Call the Durable Object method and pass the user's name and the Durable Object identifier
52+
return this.mainDo.computeMessage(userName, this.doIdentifier);
53+
}
54+
55+
// * Call the Durable Object method without using the Durable Object identifier
56+
// * @param userName - The user's name to pass to the Durable Object method
57+
58+
async simpleGreeting(userName: string) {
59+
return this.mainDo.simpleGreeting(userName);
60+
}
61+
}
62+
63+
// * Create a Durable Object class
64+
// * You can use the RpcDO class to set the Durable Object metadata
65+
66+
export class MyDurableObject extends DurableObject<Env> {
67+
constructor(ctx: DurableObjectState, env: Env) {
68+
super(ctx, env);
69+
}
70+
71+
// * Initialize the RpcDO class
72+
// * You can set the Durable Object metadata here
73+
// * It returns an instance of the RpcDO class
74+
// * @param doIdentifier - The identifier of the Durable Object
75+
76+
async setMetaData(doIdentifier: string) {
77+
return new RpcDO(this, doIdentifier);
78+
}
79+
80+
// * Function that computes a greeting message using the user's name and DO identifier
81+
// * @param userName - The user's name to include in the greeting
82+
// * @param doIdentifier - The identifier of the Durable Object
83+
84+
async computeMessage(
85+
userName: string,
86+
doIdentifier: string,
87+
): Promise<string> {
88+
console.log({
89+
userName: userName,
90+
durableObjectIdentifier: doIdentifier,
91+
});
92+
return `Hello, ${userName}! The identifier of this DO is ${doIdentifier}`;
93+
}
94+
95+
// * Function that is not in the RpcTarget
96+
// * Not every function has to be in the RpcTarget
97+
98+
private async notInRpcTarget() {
99+
return "This is not in the RpcTarget";
100+
}
101+
102+
// * Function that takes the user's name and does not use the Durable Object identifier
103+
// * @param userName - The user's name to include in the greeting
104+
105+
async simpleGreeting(userName: string) {
106+
// Call the private function that is not in the RpcTarget
107+
console.log(this.notInRpcTarget());
108+
109+
return `Hello, ${userName}! This doesn't use the DO identifier.`;
110+
}
111+
}
112+
113+
export default {
114+
async fetch(request, env, ctx): Promise<Response> {
115+
let id: DurableObjectId = env.MY_DURABLE_OBJECT.idFromName(
116+
new URL(request.url).pathname,
117+
);
118+
let stub = env.MY_DURABLE_OBJECT.get(id);
119+
120+
// * Set the Durable Object metadata using the RpcTarget
121+
// * Notice that no await is needed here
122+
123+
const rpcTarget = stub.setMetaData(id.name ?? "default");
124+
125+
// Call the Durable Object method using the RpcTarget.
126+
// The DO identifier is passed in the RpcTarget
127+
const greeting = await rpcTarget.computeMessage("world");
128+
129+
// Call the Durable Object method that does not use the Durable Object identifier
130+
const simpleGreeting = await rpcTarget.simpleGreeting("world");
131+
132+
// Clean up the RpcTarget.
133+
try {
134+
(await rpcTarget)[Symbol.dispose]?.();
135+
console.log("RpcTarget cleaned up.");
136+
} catch (e) {
137+
console.error({
138+
message: "RpcTarget could not be cleaned up.",
139+
error: String(e),
140+
errorProperties: e,
141+
});
142+
}
143+
144+
return new Response(greeting, { status: 200 });
145+
},
146+
} satisfies ExportedHandler<Env>;
147+
```
148+
149+
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.
150+
151+
```ts
152+
import { DurableObject, RpcTarget } from "cloudflare:workers";
153+
154+
// * Create an RpcDO class that extends RpcTarget
155+
// * Use this class to set the Durable Object metadata
156+
// * Pass the metadata in the Durable Object methods
157+
// * @param mainDo - The main Durable Object class
158+
// * @param doIdentifier - The identifier of the Durable Object
159+
160+
export class RpcDO extends RpcTarget {
161+
constructor(
162+
private mainDo: MyDurableObject,
163+
private doIdentifier: string,
164+
) {
165+
super();
166+
}
167+
168+
// * Pass the user's name to the Durable Object method
169+
// * @param userName - The user's name to pass to the Durable Object method
170+
171+
async computeMessage(userName: string): Promise<string> {
172+
// Call the Durable Object method and pass the user's name and the Durable Object identifier
173+
return this.mainDo.computeMessage(userName, this.doIdentifier);
174+
}
175+
176+
// * Call the Durable Object method without using the Durable Object identifier
177+
// * @param userName - The user's name to pass to the Durable Object method
178+
179+
async simpleGreeting(userName: string) {
180+
return this.mainDo.simpleGreeting(userName);
181+
}
182+
}
183+
184+
// * Create a Durable Object class
185+
// * You can use the RpcDO class to set the Durable Object metadata
186+
187+
export class MyDurableObject extends DurableObject<Env> {
188+
constructor(ctx: DurableObjectState, env: Env) {
189+
super(ctx, env);
190+
}
191+
192+
// * Initialize the RpcDO class
193+
// * You can set the Durable Object metadata here
194+
// * It returns an instance of the RpcDO class
195+
// * @param doIdentifier - The identifier of the Durable Object
196+
197+
async setMetaData(doIdentifier: string) {
198+
// Use DO storage to store the Durable Object identifier
199+
await this.ctx.storage.put("doIdentifier", doIdentifier);
200+
return new RpcDO(this, doIdentifier);
201+
}
202+
203+
// * Function that computes a greeting message using the user's name and DO identifier
204+
// * @param userName - The user's name to include in the greeting
205+
206+
async computeMessage(userName: string): Promise<string> {
207+
// Get the DO identifier from storage
208+
const doIdentifier = await this.ctx.storage.get("doIdentifier");
209+
console.log({
210+
userName: userName,
211+
durableObjectIdentifier: doIdentifier,
212+
});
213+
return `Hello, ${userName}! The identifier of this DO is ${doIdentifier}`;
214+
}
215+
216+
// * Function that is not in the RpcTarget
217+
// * Not every function has to be in the RpcTarget
218+
219+
private async notInRpcTarget() {
220+
return "This is not in the RpcTarget";
221+
}
222+
223+
// * Function that takes the user's name and does not use the Durable Object identifier
224+
// * @param userName - The user's name to include in the greeting
225+
226+
async simpleGreeting(userName: string) {
227+
// Call the private function that is not in the RpcTarget
228+
console.log(this.notInRpcTarget());
229+
230+
return `Hello, ${userName}! This doesn't use the DO identifier.`;
231+
}
232+
}
233+
234+
export default {
235+
async fetch(request, env, ctx): Promise<Response> {
236+
let id: DurableObjectId = env.MY_DURABLE_OBJECT.idFromName(
237+
new URL(request.url).pathname,
238+
);
239+
let stub = env.MY_DURABLE_OBJECT.get(id);
240+
241+
// * Set the Durable Object metadata using the RpcTarget
242+
// * Notice that no await is needed here
243+
244+
const rpcTarget = stub.setMetaData(id.name ?? "default");
245+
246+
// Call the Durable Object method using the RpcTarget.
247+
// The DO identifier is stored in the Durable Object's storage
248+
const greeting = await rpcTarget.computeMessage("world");
249+
250+
// Call the Durable Object method that does not use the Durable Object identifier
251+
const simpleGreeting = await rpcTarget.simpleGreeting("world");
252+
253+
// Clean up the RpcTarget.
254+
try {
255+
(await rpcTarget)[Symbol.dispose]?.();
256+
console.log("RpcTarget cleaned up.");
257+
} catch (e) {
258+
console.error({
259+
message: "RpcTarget could not be cleaned up.",
260+
error: String(e),
261+
errorProperties: e,
262+
});
263+
}
264+
265+
return new Response(greeting, { status: 200 });
266+
},
267+
} satisfies ExportedHandler<Env>;
268+
```

0 commit comments

Comments
 (0)