-
Notifications
You must be signed in to change notification settings - Fork 16k
[Durable Objects] Document ctx.id.jurisdiction and improve ctx.id.name scanability #30900
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
46e8301
[Durable Objects] Improve scanability of api/id.mdx name section
iglesiasbrandon 2058cf7
[Durable Objects] Document jurisdiction property and ctx.id.jurisdict…
iglesiasbrandon cff8004
[Durable Objects] Correct jurisdiction changelog date to feature roll…
iglesiasbrandon 93f3c61
[Durable Objects] Address Max's review feedback on jurisdiction docs
iglesiasbrandon 3d0adb7
Update src/content/changelog/durable-objects/2026-03-26-durable-objec…
iglesiasbrandon e647b75
Update src/content/docs/durable-objects/api/id.mdx
iglesiasbrandon 69685c6
[Durable Objects] Address remaining review feedback on jurisdiction docs
iglesiasbrandon File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
30 changes: 30 additions & 0 deletions
30
...content/changelog/durable-objects/2026-03-26-durable-object-id-jurisdiction.mdx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| --- | ||
| title: Access Durable Object jurisdiction via `ctx.id.jurisdiction` | ||
| description: Read the jurisdiction of a Durable Object from within the object itself. | ||
| products: | ||
| - durable-objects | ||
| - workers | ||
| date: 2026-03-26 | ||
| --- | ||
|
|
||
| `ctx.id.jurisdiction` inside a Durable Object now reports the [jurisdiction](/durable-objects/reference/data-location/#restrict-durable-objects-to-a-jurisdiction) the object was created in — for example `"eu"` when accessed through `env.MY_DURABLE_OBJECT.jurisdiction("eu")` — so you can make region-aware decisions without passing the jurisdiction through method arguments or persisting it in storage. For the full list of ID-construction paths that preserve `jurisdiction`, refer to the [Durable Object ID documentation](/durable-objects/api/id/#jurisdiction). | ||
|
|
||
| ```js | ||
| export class RegionalRoom extends DurableObject { | ||
| async fetch(request) { | ||
| // "eu" when accessed through env.MY_DURABLE_OBJECT.jurisdiction("eu") | ||
| const region = this.ctx.id.jurisdiction; | ||
| return new Response(`Hello from ${region ?? "the default region"}!`); | ||
| } | ||
| } | ||
|
|
||
| // Worker | ||
| export default { | ||
| async fetch(request, env) { | ||
| const stub = env.MY_DURABLE_OBJECT.jurisdiction("eu").getByName("general"); | ||
| return stub.fetch(request); | ||
| }, | ||
| }; | ||
| ``` | ||
|
|
||
| `ctx.id.jurisdiction` is `undefined` for Durable Objects that were not created in a jurisdiction-restricted namespace. Alarms scheduled before 2026-03-15 also do not have `jurisdiction` stored; to backfill the value, reschedule the alarm from a `fetch()` or RPC handler. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -80,9 +80,15 @@ assert not id1.equals(id2), "Different unique ids should never be equal." | |
|
|
||
| `name` is an optional property of a `DurableObjectId`, which returns the name that was used to create the `DurableObjectId` via [`DurableObjectNamespace::idFromName`](/durable-objects/api/namespace/#idfromname). This value is undefined if the `DurableObjectId` was constructed using [`DurableObjectNamespace::newUniqueId`](/durable-objects/api/namespace/#newuniqueid). | ||
|
|
||
| The `name` property is available on `ctx.id` inside the Durable Object when the caller uses `idFromName()` or `getByName()`. If the caller accesses the Durable Object using `idFromString()`, `ctx.id.name` will be `undefined`, even if the ID was originally created with `idFromName()`. Names longer than 1,024 bytes are not passed through and will be `undefined` on `ctx.id`. | ||
| The `name` property is also available on `ctx.id` inside the Durable Object when the caller uses `idFromName()` or `getByName()`. `ctx.id.name` will be `undefined` in the following cases: | ||
|
|
||
| - The caller accesses the Durable Object using `idFromString()`, even if the ID was originally created with `idFromName()`. | ||
| - Names longer than 1,024 bytes are not passed through to `ctx.id`. | ||
| - The Durable Object was created with `newUniqueId()`. | ||
|
|
||
| :::note[Alarms] | ||
| `ctx.id.name` is especially useful inside [alarm handlers](/durable-objects/api/alarms/), where there is no calling client to pass the name as an argument. When the alarm fires, `ctx.id.name` holds the same name the object was originally accessed with. | ||
|
|
||
| :::note | ||
| Alarms created before 2026-03-15 do not have `name` stored. When such an alarm fires, `ctx.id.name` will be `undefined`, and any new alarm scheduled from that handler will also lack a `name`. To fix this, reschedule the alarm from a `fetch()` or RPC handler where `name` is available. | ||
| ::: | ||
|
|
||
|
|
@@ -102,6 +108,20 @@ console.assert( | |
|
|
||
| </TabItem> | ||
|
|
||
| <TabItem label="TypeScript" icon="seti:typescript"> | ||
|
|
||
| ```ts | ||
| const uniqueId: DurableObjectId = env.MY_DURABLE_OBJECT.newUniqueId(); | ||
| const fromNameId: DurableObjectId = env.MY_DURABLE_OBJECT.idFromName("foo"); | ||
| console.assert(uniqueId.name === undefined, "unique ids have no name"); | ||
| console.assert( | ||
| fromNameId.name === "foo", | ||
| "name matches parameter to idFromName", | ||
| ); | ||
| ``` | ||
|
|
||
| </TabItem> | ||
|
|
||
| <TabItem label="Python" icon="seti:python"> | ||
|
|
||
| ```python | ||
|
|
@@ -115,6 +135,93 @@ assert from_name_id.name == "foo", "name matches parameter to idFromName" | |
|
|
||
| </Tabs> | ||
|
|
||
| The same `name` is available inside the Durable Object via `ctx.id.name`: | ||
|
|
||
| <Tabs> | ||
|
|
||
| <TabItem label="JavaScript" icon="seti:javascript"> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add Typescript tab too |
||
|
|
||
| ```js | ||
| import { DurableObject } from "cloudflare:workers"; | ||
|
|
||
| export class ChatRoom extends DurableObject { | ||
| async getRoomName() { | ||
| return this.ctx.id.name; // "foo" when accessed via getByName("foo") | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| </TabItem> | ||
|
|
||
| <TabItem label="TypeScript" icon="seti:typescript"> | ||
|
|
||
| ```ts | ||
| import { DurableObject } from "cloudflare:workers"; | ||
|
|
||
| export class ChatRoom extends DurableObject<Env> { | ||
| async getRoomName(): Promise<string | undefined> { | ||
| return this.ctx.id.name; // "foo" when accessed via getByName("foo") | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| </TabItem> | ||
|
|
||
| <TabItem label="Python" icon="seti:python"> | ||
|
|
||
| ```python | ||
| from workers import DurableObject | ||
|
|
||
| class ChatRoom(DurableObject): | ||
| async def get_room_name(self): | ||
| return self.ctx.id.name # "foo" when accessed via get_by_name("foo") | ||
| ``` | ||
|
|
||
| </TabItem> | ||
|
|
||
| </Tabs> | ||
|
|
||
| ### `jurisdiction` | ||
|
|
||
| `jurisdiction` is an optional property of a `DurableObjectId`, which returns the [jurisdiction](/durable-objects/reference/data-location/#restrict-durable-objects-to-a-jurisdiction) the ID is restricted to, such as `"eu"` or `"fedramp"`. The same value is available inside the Durable Object via `ctx.id.jurisdiction`, including in [alarm handlers](/durable-objects/api/alarms/) and objects accessed via `idFromString()`, so you can make region-aware decisions without passing the jurisdiction as an argument or persisting it in storage. | ||
|
|
||
| `jurisdiction` is preserved across every ID-construction path, including: | ||
|
|
||
| - IDs created from a jurisdiction-restricted subnamespace, for example `env.MY_DURABLE_OBJECT.jurisdiction("eu").idFromName("foo")` or `.newUniqueId()`. | ||
| - IDs created via `env.MY_DURABLE_OBJECT.newUniqueId({ jurisdiction: "eu" })`. | ||
| - IDs restored from a string via `idFromString()` — the jurisdiction is encoded in the string itself, so it works on any namespace binding. | ||
|
|
||
| `ctx.id.jurisdiction` is `undefined` in two cases: | ||
|
|
||
| - The Durable Object was not created in a jurisdiction-restricted namespace. | ||
| - The Durable Object's alarm was scheduled before 2026-03-15. To backfill the value, reschedule the alarm from a `fetch()` or RPC handler. | ||
|
|
||
| <Tabs> | ||
|
|
||
| <TabItem label="JavaScript" icon="seti:javascript"> | ||
|
|
||
| ```js | ||
| const plainId = env.MY_DURABLE_OBJECT.idFromName("foo"); | ||
| const euId = env.MY_DURABLE_OBJECT.jurisdiction("eu").idFromName("foo"); | ||
| console.assert(plainId.jurisdiction === undefined, "no jurisdiction set"); | ||
| console.assert(euId.jurisdiction === "eu", "jurisdiction matches namespace"); | ||
| ``` | ||
|
|
||
| </TabItem> | ||
|
|
||
| <TabItem label="Python" icon="seti:python"> | ||
|
|
||
| ```python | ||
| plain_id = env.MY_DURABLE_OBJECT.idFromName("foo") | ||
| eu_id = env.MY_DURABLE_OBJECT.jurisdiction("eu").idFromName("foo") | ||
| assert plain_id.jurisdiction is None, "no jurisdiction set" | ||
| assert eu_id.jurisdiction == "eu", "jurisdiction matches namespace" | ||
| ``` | ||
|
|
||
| </TabItem> | ||
|
|
||
| </Tabs> | ||
|
|
||
| ## Related resources | ||
|
|
||
| - [Durable Objects: Easy, Fast, Correct – Choose Three](https://blog.cloudflare.com/durable-objects-easy-fast-correct-choose-three/). | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Combine with note below for single one for alarms behavior