Cloudflare Docs
Workers
Visit Workers on GitHub
Set theme to dark (⇧+D)

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, the state.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.
  • state.waitUntil

    • While waitUntil is available within a Durable Object, it has no effect. Refer to Durable Object Lifespan for more information.
  • 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 be async) 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 a fetch()) 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 as fetch()) 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.mjs
export 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.mjs
export 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, optionsObjectoptional) : 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.
      • 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, optionsObjectoptional) : 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 to true to opt out of the default behavior. Refer to this blog post for an in-depth discussion.
      • 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 to put().
  • put(entriesObject, optionsObjectoptional) : 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 as put(key, value, options), above.

  • delete(keystring) : Promise<boolean>

    • Deletes the key and associated value. Returns true if the key existed or false if it did not.


      Supported options: Same as put(), above.

  • delete(keysArray<string>, optionsObjectoptional) : 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 as put(), 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 to list as documented below.
  • 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.
      • 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, or endKey. 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.
      • noCacheboolean

        • Same as the option to get(), above.
  • 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() and list() 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 the txn object instead of on the top-level state.storage object.

        Also supports a rollback() function that ensures any changes made during the transaction will be rolled back rather than committed. After rollback() is called, any subsequent operations on the txn object will fail with an exception. rollback() takes no parameters and returns nothing to the caller.
  • 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 as put(), 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: Like get() above, but without noCache.

  • 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 before Date.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 without noCache.

  • deleteAlarm() : Promise

    • Deletes the alarm if one exists. Does not cancel the alarm handler if it is currently executing.


      Supported options: Like put() above, but without noCache.

  • 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.

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(), or idFromString() on this namespace.

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 locationHints 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 ParameterLocation
wnamWestern North America
enamEastern North America
samSouth America
weurWestern Europe
eeurEastern Europe
apacAsia-Pacific
ocOceania
afrAfrica
meMiddle 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.