Durable Objects
Durable Objects are available to anyone with a Workers paid subscription. You can enable them for your account in the Cloudflare dashboard by navigating to “Workers” and opting in to pricing.
Background
Durable Objects provide low-latency coordination and consistent storage for the Workers platform. A given namespace can support essentially unlimited Durable Objects, with each Object having access to a transactionally consistent key-value storage API.
Durable Objects consist of two components: a class that defines a template for creating Durable Objects and a Workers script that instantiates and uses those Durable Objects. The class and the Workers script are linked together with a binding.
Learn more about using Durable Objects.
Durable Object class definition
export class DurableObject { constructor(state, env) {}
async fetch(request) {}
}
Parameters
env
- Contains environment bindings configured for the Worker script, such as KV namespaces, secrets, and other Durable Object namespaces. Note that in traditional Workers (not using Modules syntax), these same bindings appear as global variables within the Workers script. Workers that export Durable Object classes always use the Modules syntax and have bindings delivered to the constructor rather than placed in global variables.
state
- Passed from the runtime to provide access to the Durable Object’s storage as well as various metadata about the Object.
Properties of state
state.id
DurableObjectId
- The ID of this Durable Object. It can be converted into a hex string using its
.toString()
method. Inside a Durable Object, thestate.id.name
property is not defined. If you need access to the name, explicitly pass it in the fetch request to the Durable Object, for example, a query parameter in the URL.
- The ID of this Durable Object. It can be converted into a hex string using its
state.waitUntil
- While
waitUntil
is available within a Durable Object, it has no effect. Refer to Durable Object Lifespan for more information.
- While
state.storage
- Contains methods for accessing persistent storage via the transactional storage API. Refer to Transactional Storage API for a detailed reference.
state.blockConcurrencyWhile(callbackFunction())
:Promise
Executes
callback()
(which may beasync
) while blocking any other events from being delivered to the object until the callback completes. This allows you to execute some code that performs I/O (such as afetch()
) with the guarantee that the object’s state will not unexpectedly change as a result of concurrent events. All events that were not explicitly initiated as part of the callback itself will be blocked. This includes not only new incoming requests, but also responses to outgoing requests (such asfetch()
) that were initiated outside of the callback. Once the callback completes, these events will be delivered.state.blockConcurrencyWhile()
is especially useful within the constructor of your Object to perform initialization that must occur before any requests are delivered.If the callback throws an exception, the Object will be terminated and reset. This ensures that the Object cannot be left stuck in an uninitialized state if something fails unexpectedly. To avoid this behavior, wrap the body of your callback in a
try
/catch
block to ensure it cannot throw an exception.The value returned by the callback becomes the value returned by
blockConcurrencyWhile()
itself.
Durable Object lifespan
A Durable Object remains active until all asynchronous I/O, including Promises, within the Durable Object has resolved. This is true for all HTTP and/or WebSocket connections, except for failure scenarios, which may include unhandled runtime exceptions or exceeding the CPU limit.
From a Workers perspective, this is similar to enqueuing tasks with FetchEvent.waitUntil
. For example, in order to send a POST request without delaying a Response
, a Worker script may include the following code:
worker.mjsexport default { fetch(req, env, ctx) { // Send a non-blocking POST request. // ~> Completes before the Worker exits. ctx.waitUntil( fetch('https://.../logs', { method: 'POST', body: JSON.stringify({ url: req.url, // ... }), }) );
return new Response('OK'); },
};
The same functionality can be achieved in a Durable Object, simply by omitting the call to ctx.waitUntil()
for the POST request. The request will complete before the Durable Object exits:
durable.mjsexport class Example { fetch(req) { // NOTE: Omits `await` intentionally. // ~> Does not block `Response` output // ~> Will still wait for POST to complete fetch('https://.../logs', { method: 'POST', body: JSON.stringify({ url: req.url, // ... }), });
return new Response('OK'); }
}
In-memory state
A Durable Object may be evicted from memory any time, causing a loss of all transient (in-memory) state. To persistently store state your Durable Object might need in the future, use the Transactional Storage API.
A Durable Object is given 30 seconds of additional CPU time for every request it processes, including WebSocket messages. In the absence of failures, in-memory state should not be reset after less than 10 seconds of inactivity.
Transactional storage API
The transactional storage API is accessible via the state.storage
object passed to the Durable Object constructor.
Methods
Each method is implicitly wrapped inside a transaction, such that its results are atomic and isolated from all other storage operations, even when accessing multiple key-value pairs.
get(keystring, optionsObject )
:Promise<any>
Retrieves the value associated with the given key. The type of the returned value will be whatever was previously written for the key, or undefined if the key does not exist.
Supported options:
allowConcurrencyboolean
- By default, the system will pause delivery of I/O events to the object while a storage operation is in progress, in order to avoid unexpected race conditions. Pass
allowConcurrency: true
to opt out of this behavior and allow concurrent events to be delivered.
- By default, the system will pause delivery of I/O events to the object while a storage operation is in progress, in order to avoid unexpected race conditions. Pass
noCacheboolean
- If true, then the key/value will not be inserted into the in-memory cache. If the key is already in the cache, the cached value will be returned, but its last-used time will not be updated. Use this when you expect this key will not be used again in the near future. This flag is only a hint: it will never change the semantics of your code, but it may affect performance.
get(keysArray<string>, optionsObject)
:Promise<Map<string, any>>
Retrieves the values associated with each of the provided keys. The type of each returned value in the Map will be whatever was previously written for the corresponding key. Results in the Map will be sorted in increasing order of their UTF-8 encodings, with any requested keys that do not exist being omitted. Supports up to 128 keys at a time.
Supported options:Same as
get(key, options)
, above.
put(keystring, valueany, optionsObject )
:Promise
Stores the value and associates it with the given key. The value can be any type supported by the structured clone algorithm, which is true of most types. Keys are limited to a max size of 2048 bytes and values are limited to 128 KiB (131072 bytes).
Supported options:
allowUnconfirmedboolean
- By default, the system will pause outgoing network messages from the Durable Object until all previous writes have been confirmed flushed to disk. In the unlikely event that the write fails, the system will reset the Object, discard all outgoing messages, and respond to any clients with errors instead. This way, Durable Objects can continue executing in parallel with a write operation, without having to worry about prematurely confirming writes, because it is impossible for any external party to observe the Object’s actions unless the write actually succeeds. However, this does mean that after any write, subsequent network messages may be slightly delayed. Some applications may consider it acceptable to communicate on the basis of unconfirmed writes and may prefer to allow network traffic immediately. In this case, set
allowUnconfirmed
totrue
to opt out of the default behavior. Refer to this blog post for an in-depth discussion.
- By default, the system will pause outgoing network messages from the Durable Object until all previous writes have been confirmed flushed to disk. In the unlikely event that the write fails, the system will reset the Object, discard all outgoing messages, and respond to any clients with errors instead. This way, Durable Objects can continue executing in parallel with a write operation, without having to worry about prematurely confirming writes, because it is impossible for any external party to observe the Object’s actions unless the write actually succeeds. However, this does mean that after any write, subsequent network messages may be slightly delayed. Some applications may consider it acceptable to communicate on the basis of unconfirmed writes and may prefer to allow network traffic immediately. In this case, set
noCacheboolean
- If true, then the key/value will be discarded from memory as soon as it has completed writing to disk. Use this when you expect this key will not be used again in the near future. This flag is only a hint: it will never change the semantics of your code, but it may affect performance. In particular, if you
get()
the key before the write to disk has completed, the copy from the write buffer will be returned, thus ensuring consistency with the latest call toput()
.
- If true, then the key/value will be discarded from memory as soon as it has completed writing to disk. Use this when you expect this key will not be used again in the near future. This flag is only a hint: it will never change the semantics of your code, but it may affect performance. In particular, if you
put(entriesObject, optionsObject )
:Promise
Takes an Object and stores each of its keys and values to storage. Each value can be any type supported by the structured clone algorithm, which is true of most types. Supports up to 128 key-value pairs at a time. Each key is limited to a maximum size of 2048 bytes and each value is limited to 128 KiB (131072 bytes).
Supported options: Same asput(key, value, options)
, above.
delete(keystring)
:Promise<boolean>
Deletes the key and associated value. Returns
true
if the key existed orfalse
if it did not.
Supported options: Same asput()
, above.
delete(keysArray<string>, optionsObject )
:Promise<number>
Deletes the provided keys and their associated values. Supports up to 128 keys at a time. Returns a count of the number of key-value pairs deleted.
Supported options: Same asput()
, above.
list()
:Promise<Map<string, any>>
- Returns all keys and values associated with the current Durable Object in ascending sorted order based on the keys’ UTF-8 encodings. The type of each returned value in the Map will be whatever was previously written for the corresponding key. Be aware of how much data may be stored in your Durable Object before calling this version of
list
without options because all the data will be loaded into the Durable Object’s memory, potentially hitting its limit. If that is a concern, pass options tolist
as documented below.
- Returns all keys and values associated with the current Durable Object in ascending sorted order based on the keys’ UTF-8 encodings. The type of each returned value in the Map will be whatever was previously written for the corresponding key. Be aware of how much data may be stored in your Durable Object before calling this version of
list(optionsObject)
:Promise<Map<string, any>>
Returns keys and values associated with the current Durable Object according to the parameters in the provided options object.
Supported options:startstring
- Key at which the list results should start, inclusive.
startAfterstring
- Key after which the list results should start, exclusive. Cannot be used simultaneously with
start
.
- Key after which the list results should start, exclusive. Cannot be used simultaneously with
endstring
- Key at which the list results should end, exclusive.
prefixstring
- Restricts results to only include key-value pairs whose keys begin with the prefix.
reverseboolean
- If true, return results in descending order instead of the default ascending order.
- Note that enabling this does not change the meaning of
start
,startKey
, orendKey
.start
still defines the smallest key in lexicographic order that can be returned (inclusive), effectively serving as the endpoint for a reverse-order list.end
still defines the largest key in lexicographic order that the list should consider (exclusive), effectively serving as the starting point for a reverse-order list.
limitnumber
- Maximum number of key-value pairs to return.
allowConcurrencyboolean
- Same as the option to
get()
, above.
- Same as the option to
noCacheboolean
- Same as the option to
get()
, above.
- Same as the option to
transaction(closureFunction(txn))
:Promise
Runs the sequence of storage operations called on
txn
in a single transaction that either commits successfully or aborts.txn
- Provides access to the
put()
,get()
,delete()
andlist()
methods documented above to run in the current transaction context. In order to get transactional behavior within a transaction closure, you must call the methods on thetxn
object instead of on the top-levelstate.storage
object.
Also supports arollback()
function that ensures any changes made during the transaction will be rolled back rather than committed. Afterrollback()
is called, any subsequent operations on thetxn
object will fail with an exception.rollback()
takes no parameters and returns nothing to the caller.
- Provides access to the
deleteAll()
:Promise
Deletes all keys and associated values, effectively deallocating all storage used by the Durable Object. In the event of a failure while the
deleteAll()
operation is still in flight, it may be that only a subset of the data is properly deleted.
Supported options: Same asput()
, above.
getAlarm()
:Promise<Number | null>
Retrieves the current alarm time (if set) as integer milliseconds since epoch. The alarm is considered to be set if it has not started, or if it has failed and any retry has not begun. If no alarm is set,
getAlarm()
returns null.
Supported options: Likeget()
above, but withoutnoCache
.
setAlarm(scheduledTimeDate | number)
:Promise
Sets the current alarm time, accepting either a JS Date, or integer milliseconds since epoch.
If
setAlarm()
is called with a time equal to or beforeDate.now()
, the alarm will be scheduled for asynchronous execution in the immediate future. If the alarm handler is currently executing in this case, it will not be canceled. Alarms can be set to millisecond granularity and will usually execute within a few milliseconds after the set time, but can be delayed by up to a minute due to maintenance or failures while failover takes place.Supported options: Like
put()
above, but withoutnoCache
.
deleteAlarm()
:Promise
Deletes the alarm if one exists. Does not cancel the alarm handler if it is currently executing.
Supported options: Likeput()
above, but withoutnoCache
.
sync()
:Promise
Synchronizes any pending writes to disk.
This is similar to normal behavior from automatic write coalescing. If there are any pending writes in the write buffer (including those submitted with
allowUnconfirmed
), the returned promise will resolve when they complete. If there are no pending writes, the returned promise will be already resolved.Supported options: None.
alarm()
handler method
The system calls the alarm()
handler method when a scheduled alarm time is reached. The alarm()
handler has guaranteed at-least-once execution and will be retried upon failure using exponential backoff, starting at 2 seconds delay for up to 6 retries. Retries will be performed if the method fails with an uncaught exception. Calling deleteAlarm()
inside the alarm()
handler may prevent retries on a best-effort basis, but is not guaranteed.
The method takes no parameters, does not return a result, and can be async
.
fetch()
handler method
The system calls the fetch()
method of a Durable Object namespace when an HTTP request is sent to the Object. These requests are not sent from the public Internet, but from other Workers using a Durable Object namespace binding.
The method takes a Request
as the parameter and returns a Response
(or a Promise
for a Response
).
If the method fails with an uncaught exception, the exception will be thrown into the calling Worker that made the fetch()
request.
Accessing a Durable Object from a Worker
To access a Durable Object from a Worker, you must first configure the Worker with a binding for a Durable Object namespace. The namespace is, in turn, configured to use a particular class and controls access to instances of that class.
Namespace bindings have two jobs: generating Object IDs and connecting to Objects.
Generating IDs randomly
let id = OBJECT_NAMESPACE.newUniqueId();
The newUniqueId()
method on a Durable Object namespace creates a new Object ID randomly. This method will never return the same ID twice, and thus, it is guaranteed that the Object does not yet exist and has never existed at the time the method returns.
When generating an ID randomly, you need to store the ID somewhere in order to be able to reach the same object again in the future. You could, for example, store the ID in Workers KV, in an external database, or in a cookie in the user’s browser.
Unique IDs are unguessable, therefore they can be used in URL-based access control.
To store the ID in external storage, use its .toString()
method to convert it into a hexadecimal string and OBJECT_NAMESPACE.idFromString()
to convert the string back into an ID later.
Deriving IDs from names
let id = OBJECT_NAMESPACE.idFromName(name);
Parameters
name
string
- The Object name, an arbitrary string from which the ID is derived.
This method derives a unique object ID from the given name string. It will always return the same ID when given the same name as input.
Parsing previously-created IDs from strings
let id = OBJECT_NAMESPACE.idFromString(hexId);
Parameters
hexId
string
- An ID string constructed by calling the
.toString()
method of an existing ID.
- An ID string constructed by calling the
This method parses an ID that was previously stringified. This is useful in particular with IDs created using newUniqueId()
, as these IDs need to be stored somewhere, probably as a string.
A stringified object ID is a 64-digit hexadecimal number. However, not all 64-digit hex numbers are valid IDs. This method will throw if it is passed an ID that was not originally created by newUniqueId()
or idFromName()
. It will also throw if the ID was originally created for a different namespace.
Obtaining an Object stub
let stub = OBJECT_NAMESPACE.get(id);
Parameters
id
DurableObjectId
- An ID constructed using
newUniqueId()
,idFromName()
, oridFromString()
on this namespace.
- An ID constructed using
This method constructs an Object stub, which is a local client that provides access to a remote Durable Object.
If the remote Object does not already exist, it will be created. Thus, there will always be an Object accessible from the stub.
This method always returns the stub immediately, before it has connected to the remote object. This allows you to begin making requests to the object right away, without waiting for a network round trip.
Providing a location hint
Durable Objects do not currently move between geographical regions after they are created1. By default, Durable Objects are created close to the first client that accesses them via GET
. To manually create Durable Objects in another location, provide an optional locationHint
parameter to GET
. Only the first call to GET
for a particular object will respect the hint.
let stub = OBJECT_NAMESPACE.get(id, { locationHint: 'enam' });
The following locationHint
s are supported. Note that hints are a best effort and not a guarantee. Durable Objects do not currently run in all of the locations below. The closest nearby region will be used until those locations are fully supported.
Location Hint Parameter | Location |
---|---|
wnam | Western North America |
enam | Eastern North America |
sam | South America |
weur | Western Europe |
eeur | Eastern Europe |
apac | Asia-Pacific |
oc | Oceania |
afr | Africa |
me | Middle East |
1 Dynamic relocation of existing Durable Objects is planned for the future.
Restricting objects to a jurisdiction
Durable Objects can be created so that they only run and store data within a specific jurisdiction to comply with local regulations such as the GDPR or FedRAMP. To use a jurisdiction, first create a jursidictional subnamespace:
let subnamespace = OBJECT_NAMESPACE.jurisdiction('eu');
A jursidictional subnamespace works exactly like a normal Durable Object namespace (OBJECT_NAMESPACE
above), except that IDs created within them permanently encode the jurisdiction that was used to create the subnamespace. Additionally, the idFromString
and get
methods will throw an exception if the IDs passed into them are not within the subnamespace’s jurisdiction. Once you have a subnamespace you can use all of the namespace methods documented above.
To create a new Object ID that will only run and persist data within the jurisdiction:
let id = subnamespace.newUniqueId();
To derive a unique object ID from the given name string that will only run and persist data within the jurisdiction:
let id = subnamespace.idFromName(name);
To parse a previously-created ID from a string:
let id = subnamespace.idFromString(id);
To obtain an object stub:
let stub = subnamespace.get(id)
While you cannot use an ID from a different jurisdiction in a subnamespace’s idFromString
or get
methods, you can use any valid ID in the top-level namespace’s methods. Object IDs created with a jurisdiction will still only run and persist data within the jurisdiction.
let id = subnamespace.idFromName(name);
// This is valid.
OBJECT_NAMESPACE.idFromString(id.toString())
// And so is this.
OBJECT_NAMESPACE.get(id)
Your Workers may still access Objects constrained to a jurisdiction from anywhere in the world. The jurisdiction constraint only controls where the Durable Object itself runs and persists data. Consider using Regional Services to control the regions from which Cloudflare responds to requests.
The currently supported jurisdictions are eu
(the European Union) and fedramp
(FedRAMP).
Object stubs
A Durable Object stub is a client object used to send requests to a remote Durable Object.
A stub is created using OBJECT_NAMESPACE.get(id)
(above).
Stubs implement E-order semantics. When you make multiple calls to the same stub, it is guaranteed that the calls will be delivered to the remote Object in the order in which you made them. This ordering guarantee often makes many distributed programming problems easier. However, there is a cost: due to random network disruptions or other transient issues, a stub may become disconnected from its remote Object. Once a stub is disconnected, it is permanently broken, and all in-flight calls and future calls will fail with exceptions. To make new requests to the Durable Object, you must call OBJECT_NAMESPACE.get(id)
again to get a new stub, keeping in mind that there are no ordering guarantees between requests to the new stub versus the old one. If ordering is not a concern, you can create a new stub for every request.
Sending HTTP requests
let response = await stub.fetch(request);
let response = await stub.fetch(url, options);
The fetch()
method of a stub has the exact same signature as the global fetch
. However, instead of sending an HTTP request to the Internet, the request is always sent to the Durable Object to which the stub points.
Any uncaught exceptions thrown by the Durable Object’s fetch()
handler will be propagated to the caller’s fetch()
promise. Furthermore, if an uncaught exception is thrown by the Durable Object’s fetch()
handler, then the exception propagated to the caller’s fetch()
promise will include a property .remote
, which will be set to True
. If the caller’s fetch()
failed as a result of being unable to reach the Durable Object, the exception thrown to the caller’s fetch()
will not have the .remote
property, indicating the exception was not generated remotely.
Listing Durable Objects
The Cloudflare REST API supports retrieving a list of Durable Objects within a namespace and a list of namespaces associated with an account.