diff --git a/src/content/changelog/durable-objects/2026-03-26-durable-object-id-jurisdiction.mdx b/src/content/changelog/durable-objects/2026-03-26-durable-object-id-jurisdiction.mdx new file mode 100644 index 000000000000000..a9a773b54a927f2 --- /dev/null +++ b/src/content/changelog/durable-objects/2026-03-26-durable-object-id-jurisdiction.mdx @@ -0,0 +1,39 @@ +--- +title: Access Durable Object jurisdiction via `ctx.id.jurisdiction` +description: Read the jurisdiction of a jurisdiction-restricted 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) that the object was created in, matching the value seen client-side. If a Worker accesses a Durable Object through a jurisdiction-restricted namespace — for example `env.MY_DURABLE_OBJECT.jurisdiction("eu")` — the same jurisdiction is available on `ctx.id.jurisdiction` inside the object, so you can make region-aware decisions without passing the jurisdiction through method arguments 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. +- IDs observed inside [alarm handlers](/durable-objects/api/alarms/) for alarms scheduled on 2026-03-15 or later. + +```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. + +For more information, refer to the [Durable Object ID documentation](/durable-objects/api/id/#jurisdiction). diff --git a/src/content/docs/durable-objects/api/id.mdx b/src/content/docs/durable-objects/api/id.mdx index 53f002be2f6f63c..ed3f1d20ecca1d5 100644 --- a/src/content/docs/durable-objects/api/id.mdx +++ b/src/content/docs/durable-objects/api/id.mdx @@ -80,7 +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()`. + +:::tip[Useful for 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. @@ -115,6 +123,73 @@ assert from_name_id.name == "foo", "name matches parameter to idFromName" +The same `name` is available inside the Durable Object via `ctx.id.name`: + + + + + +```js +import { DurableObject } from "cloudflare:workers"; + +export class ChatRoom extends DurableObject { + async getRoomName() { + return this.ctx.id.name; // "foo" when accessed via getByName("foo") + } +} +``` + + + + + +```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") +``` + + + + + +### `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 after restoring an ID via `idFromString()`, so you can make region-aware decisions without passing the jurisdiction as an argument or persisting it in storage. + +`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. + + + + + +```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"); +``` + + + + + +```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" +``` + + + + + ## Related resources - [Durable Objects: Easy, Fast, Correct – Choose Three](https://blog.cloudflare.com/durable-objects-easy-fast-correct-choose-three/). diff --git a/src/content/docs/durable-objects/reference/data-location.mdx b/src/content/docs/durable-objects/reference/data-location.mdx index 4f4ced530d3685f..2b50b4aa293d5db 100644 --- a/src/content/docs/durable-objects/reference/data-location.mdx +++ b/src/content/docs/durable-objects/reference/data-location.mdx @@ -58,11 +58,15 @@ Note that it is also possible to specify a jurisdiction by creating an individua ### Supported locations -| Parameter | Location | -| --------- | ---------------------------- | -| eu | The European Union | +| Parameter | Location | +| --------- | ------------------------------ | +| eu | The European Union | | fedramp | FedRAMP-compliant data centers | +### Read the jurisdiction from inside a Durable Object + +The jurisdiction of a Durable Object is available inside the object via [`ctx.id.jurisdiction`](/durable-objects/api/id/#jurisdiction). The value is preserved across `toString()` and `idFromString()` round-trips and is also available inside [alarm handlers](/durable-objects/api/alarms/) for alarms scheduled on 2026-03-15 or later, which makes it suitable for region-aware logic inside the Durable Object. + ## Provide a location hint Durable Objects, as with any stateful API, will often add response latency as requests must be forwarded to the data center where the Durable Object, or state, is located. @@ -102,10 +106,14 @@ Hints are a best effort and not a guarantee. Unlike with jurisdictions, Durable | afr | Africa 2 | | me | Middle East 2 | -1 Dynamic relocation of existing Durable Objects is planned for the future. +1 Dynamic relocation of existing Durable Objects is planned for the +future. -2 Durable Objects currently do not spawn in this location. Instead, the Durable Object will spawn in a nearby location which does support Durable Objects. For example, Durable Objects hinted to South America spawn in Eastern North America instead. +2 Durable Objects currently do not spawn in this location. Instead, +the Durable Object will spawn in a nearby location which does support Durable +Objects. For example, Durable Objects hinted to South America spawn in Eastern +North America instead. ## Additional resources -- You can find our more about where Durable Objects are located using the website: [Where Durable Objects Live](https://where.durableobjects.live/). \ No newline at end of file +- You can find our more about where Durable Objects are located using the website: [Where Durable Objects Live](https://where.durableobjects.live/).