diff --git a/public/__redirects b/public/__redirects
index 4423a53dbcfd859..892ff0611dc922e 100644
--- a/public/__redirects
+++ b/public/__redirects
@@ -388,6 +388,7 @@
/durable-objects/api/hibernatable-websockets-api/ /durable-objects/best-practices/websockets/ 301
/durable-objects/api/alarms-in-durable-objects/ /durable-objects/api/alarms/ 301
/durable-objects/api/websockets/ /durable-objects/best-practices/websockets/ 301
+/durable-objects/api/sql-storage/ /durable-objects/api/storage-api/ 301
/durable-objects/platform/data-location/ /durable-objects/reference/data-location/ 301
/durable-objects/platform/environments/ /durable-objects/reference/environments/ 301
/durable-objects/platform/troubleshooting/ /durable-objects/observability/troubleshooting/ 301
@@ -406,7 +407,9 @@
/durable-objects/platform/changelog/ /durable-objects/release-notes/ 301
/durable-objects/changelog/ /durable-objects/release-notes/ 301
/durable-objects/glossary/ /durable-objects/reference/glossary/ 301
-/durable-objects/get-started/walkthrough/ /durable-objects/get-started/tutorial/ 301
+/durable-objects/get-started/walkthrough/ /durable-objects/get-started/ 301
+/durable-objects/get-started/tutorial/ /durable-objects/get-started/ 301
+/durable-objects/get-started/tutorial-with-sql-api/ /durable-objects/get-started/ 301
/durable-objects/get-started/video-series/intro/ /durable-objects/video-tutorials/ 301
/durable-objects/get-started/video-series/app-frontend/ /durable-objects/video-tutorials/ 301
/durable-objects/get-started/video-series/deploy-app/ /durable-objects/video-tutorials/ 301
diff --git a/src/content/changelog/durable-objects/2025-04-07-durable-objects-free-tier.mdx b/src/content/changelog/durable-objects/2025-04-07-durable-objects-free-tier.mdx
new file mode 100644
index 000000000000000..80c98d05c446f35
--- /dev/null
+++ b/src/content/changelog/durable-objects/2025-04-07-durable-objects-free-tier.mdx
@@ -0,0 +1,48 @@
+---
+title: Durable Objects on Workers Free plan
+description: Durable Objects now available on Workers Free plan.
+products:
+ - durable-objects
+ - workers
+date: 2025-04-07T06:00:00Z
+---
+
+Durable Objects can now be used with zero commitment on the [Workers Free plan](/workers/platform/pricing/) allowing you to build AI agents with [Agents SDK](/agents/), collaboration tools, and real-time applications like chat or multiplayer games.
+
+Durable Objects let you build stateful, serverless applications with millions of tiny coordination instances that run your application code alongside (in the same thread!) your durable storage. Each Durable Object can access its own SQLite database through a [Storage API](/durable-objects/best-practices/access-durable-objects-storage/). A Durable Object class is defined in a Worker script encapsulating the Durable Object's behavior when accessed from a Worker. To try the code below, click the button:
+
+[](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/templates/tree/staging/hello-world-do-template)
+
+```js
+import { DurableObject } from "cloudflare:workers";
+
+// Durable Object
+export class MyDurableObject extends DurableObject {
+ ...
+ async sayHello(name) {
+ return `Hello, ${name}!`;
+ }
+}
+
+// Worker
+export default {
+ async fetch(request, env) {
+ // Every unique ID refers to an individual instance of the Durable Object class
+ const id = env.MY_DURABLE_OBJECT.idFromName("foo");
+
+ // A stub is a client used to invoke methods on the Durable Object
+ const stub = env.MY_DURABLE_OBJECT.get(id);
+
+ // Methods on the Durable Object are invoked via the stub
+ const response = await stub.sayHello("world");
+
+ return response;
+ },
+};
+```
+
+Free plan [limits](/durable-objects/platform/pricing/) apply to Durable Objects compute and storage usage. Limits allow developers to build real-world applications, with every Worker request able to call a Durable Object on the free plan.
+
+For more information, checkout:
+- [Documentation](/durable-objects/what-are-durable-objects/)
+- [Zero-latency SQLite storage in every Durable Object blog](https://blog.cloudflare.com/sqlite-in-durable-objects/)
diff --git a/src/content/changelog/durable-objects/2025-04-07-sqlite-in-durable-objects-ga.mdx b/src/content/changelog/durable-objects/2025-04-07-sqlite-in-durable-objects-ga.mdx
new file mode 100644
index 000000000000000..ee521539a5b837f
--- /dev/null
+++ b/src/content/changelog/durable-objects/2025-04-07-sqlite-in-durable-objects-ga.mdx
@@ -0,0 +1,33 @@
+---
+title: SQLite in Durable Objects GA with 10GB storage per object
+description: SQLite-backed Durable Objects are generally available.
+products:
+ - durable-objects
+ - workers
+date: 2025-04-07T06:00:00Z
+---
+
+SQLite in Durable Objects is now generally available (GA) with 10GB SQLite database per Durable Object. Since the [public beta](https://blog.cloudflare.com/sqlite-in-durable-objects/) in September 2024, we've added feature parity and robustness for the SQLite storage backend compared to the preexisting key-value (KV) storage backend for Durable Objects.
+
+SQLite-backed Durable Objects are recommended for all new Durable Object classes, using `new_sqlite_classes` [Wrangler configuration](/durable-objects/best-practices/access-durable-objects-storage/#create-sqlite-backed-durable-object-class). Only SQLite-backed Durable Objects have access to Storage API's [SQL](/durable-objects/api/storage-api/#sql-api) and [point-in-time recovery](/durable-objects/api/storage-api/#pitr-point-in-time-recovery-api) methods, which provide relational data modeling, SQL querying, and better data management.
+
+```js
+export class MyDurableObject extends DurableObject {
+ sql: SqlStorage
+ constructor(ctx: DurableObjectState, env: Env) {
+ super(ctx, env);
+ this.sql = ctx.storage.sql;
+ }
+
+ async sayHello() {
+ let result = this.sql
+ .exec("SELECT 'Hello, World!' AS greeting")
+ .one();
+ return result.greeting;
+ }
+}
+```
+
+KV-backed Durable Objects remain for backwards compatibility, and a migration path from key-value storage to SQL storage for existing Durable Object classes will be offered in the future.
+
+For more details on SQLite storage, checkout [Zero-latency SQLite storage in every Durable Object blog](https://blog.cloudflare.com/sqlite-in-durable-objects/).
diff --git a/src/content/docs/agents/api-reference/schedule-tasks.mdx b/src/content/docs/agents/api-reference/schedule-tasks.mdx
index 8d39c8ab52a9f29..382133b9edea3c6 100644
--- a/src/content/docs/agents/api-reference/schedule-tasks.mdx
+++ b/src/content/docs/agents/api-reference/schedule-tasks.mdx
@@ -70,7 +70,7 @@ Calling `await this.schedule` returns a `Schedule`, which includes the task's ra
:::note[Maximum scheduled tasks]
-Each task is mapped to a row in the Agent's underlying [SQLite database](/durable-objects/api/sql-storage/), which means that each task can be up to 2 MB in size. The maximum number of tasks must be `(task_size * tasks) + all_other_state < maximum_database_size` (currently 1GB per Agent).
+Each task is mapped to a row in the Agent's underlying [SQLite database](/durable-objects/api/storage-api/), which means that each task can be up to 2 MB in size. The maximum number of tasks must be `(task_size * tasks) + all_other_state < maximum_database_size` (currently 1GB per Agent).
:::
diff --git a/src/content/docs/agents/api-reference/store-and-sync-state.mdx b/src/content/docs/agents/api-reference/store-and-sync-state.mdx
index fea3f3949f54de9..f95db29924eb02c 100644
--- a/src/content/docs/agents/api-reference/store-and-sync-state.mdx
+++ b/src/content/docs/agents/api-reference/store-and-sync-state.mdx
@@ -227,7 +227,7 @@ Learn more about the zero-latency SQL storage that powers both Agents and Durabl
:::
-The SQL API exposed to an Agent is similar to the one [within Durable Objects](/durable-objects/api/sql-storage/): Durable Object SQL methods available on `this.ctx.storage.sql`. You can use the same SQL queries with the Agent's database, create tables, and query data, just as you would with Durable Objects or [D1](/d1/).
+The SQL API exposed to an Agent is similar to the one [within Durable Objects](/durable-objects/api/storage-api/#sql-api): Durable Object SQL methods available on `this.ctx.storage.sql`. You can use the same SQL queries with the Agent's database, create tables, and query data, just as you would with Durable Objects or [D1](/d1/).
### Use Agent state as model context
diff --git a/src/content/docs/browser-rendering/workers-binding-api/browser-rendering-with-DO.mdx b/src/content/docs/browser-rendering/workers-binding-api/browser-rendering-with-DO.mdx
index 779b94d5ffe9b37..8112b9440bff5ff 100644
--- a/src/content/docs/browser-rendering/workers-binding-api/browser-rendering-with-DO.mdx
+++ b/src/content/docs/browser-rendering/workers-binding-api/browser-rendering-with-DO.mdx
@@ -33,15 +33,7 @@ Create a new Worker project named `browser-worker` by running:
args={"browser-worker"}
/>
-## 2. Enable Durable Objects in the dashboard
-
-To enable Durable Objects, you will need to purchase the Workers Paid plan:
-
-1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/), and select your account.
-2. Go to **Workers & Pages** > **Plans**.
-3. Select **Purchase Workers Paid** and complete the payment process to enable Durable Objects.
-
-## 3. Install Puppeteer
+## 2. Install Puppeteer
In your `browser-worker` directory, install Cloudflare’s [fork of Puppeteer](/browser-rendering/platform/puppeteer/):
@@ -49,7 +41,7 @@ In your `browser-worker` directory, install Cloudflare’s [fork of Puppeteer](/
npm install @cloudflare/puppeteer --save-dev
```
-## 4. Create a R2 bucket
+## 3. Create a R2 bucket
Create two R2 buckets, one for production, and one for development.
@@ -68,7 +60,7 @@ wrangler r2 bucket list
After running the `list` command, you will see all bucket names, including the ones you have just created.
-## 5. Configure your Wrangler configuration file
+## 4. Configure your Wrangler configuration file
Configure your `browser-worker` project's [Wrangler configuration file](/workers/wrangler/configuration/) by adding a browser [binding](/workers/runtime-apis/bindings/) and a [Node.js compatibility flag](/workers/configuration/compatibility-flags/#nodejs-compatibility-flag). Browser bindings allow for communication between a Worker and a headless browser which allows you to do actions such as taking a screenshot, generating a PDF and more.
@@ -100,13 +92,13 @@ class_name = "Browser"
[[migrations]]
tag = "v1" # Should be unique for each entry
-new_classes = ["Browser"] # Array of new classes
+new_sqlite_classes = ["Browser"] # Array of new classes
```
-## 6. Code
+## 5. Code
The code below uses Durable Object to instantiate a browser using Puppeteer. It then opens a series of web pages with different resolutions, takes a screenshot of each, and uploads it to R2.
@@ -219,11 +211,11 @@ export class Browser {
}
```
-## 7. Test
+## 6. Test
Run [`npx wrangler dev --remote`](/workers/wrangler/commands/#dev) to test your Worker remotely before deploying to Cloudflare's global network. Local mode support does not exist for Browser Rendering so `--remote` is required.
-## 8. Deploy
+## 7. Deploy
Run [`npx wrangler deploy`](/workers/wrangler/commands/#deploy) to deploy your Worker to the Cloudflare global network.
diff --git a/src/content/docs/durable-objects/api/sql-storage.mdx b/src/content/docs/durable-objects/api/sql-storage.mdx
deleted file mode 100644
index 65a6725cc35b68e..000000000000000
--- a/src/content/docs/durable-objects/api/sql-storage.mdx
+++ /dev/null
@@ -1,204 +0,0 @@
----
-title: SQL Storage
-pcx_content_type: concept
-sidebar:
- order: 6
-
----
-
-import { Render, Type, MetaInfo, GlossaryTooltip } from "~/components";
-
-The `SqlStorage` interface encapsulates methods that modify the SQLite database embedded within a Durable Object. The `SqlStorage` interface is accessible via the `sql` property of `DurableObjectStorage` class.
-
-For example, using `sql.exec()`, a user can create a table, then insert rows into the table.
-
-```ts
-import { DurableObject } from "cloudflare:workers";
-
-export class MyDurableObject extends DurableObject {
- sql: SqlStorage;
- constructor(ctx: DurableObjectState, env: Env) {
- super(ctx, env);
- this.sql = ctx.storage.sql;
-
- this.sql.exec(`CREATE TABLE IF NOT EXISTS artist(
- artistid INTEGER PRIMARY KEY,
- artistname TEXT
- );INSERT INTO artist (artistid, artistname) VALUES
- (123, 'Alice'),
- (456, 'Bob'),
- (789, 'Charlie');`
- );
- }
-}
-```
-
-:::note[SQLite in Durable Objects Beta]
-SQL API methods accessed with `ctx.storage.sql` are only allowed on [Durable Object classes with SQLite storage backend](/durable-objects/reference/durable-objects-migrations/#enable-sqlite-storage-backend-on-new-durable-object-class-migration) and will return an error if called on Durable Object classes with a key-value storage backend.
-:::
-
-:::note[Writing to indexes or virtual tables]
-When writing data, every index counts as an additional row. However, indexes may be beneficial for read-heavy use cases. Refer to [Index for SQLite Durable Objects](/durable-objects/best-practices/access-durable-objects-storage/#index-for-sqlite-durable-objects).
-
-Writing data to [SQLite virtual tables](https://www.sqlite.org/vtab.html) also counts towards rows written.
-:::
-
-Specifically for Durable Object classes with SQLite storage backend, KV operations which were previously asynchronous (for example, [`get`](/durable-objects/api/storage-api/#get), [`put`](/durable-objects/api/storage-api/#put), [`delete`](/durable-objects/api/storage-api/#delete), [`deleteAll`](/durable-objects/api/storage-api/#deleteall), [`list`](/durable-objects/api/storage-api/#list)) are synchronous, even though they return promises. These methods will have completed their operations before they return the promise.
-
-## Methods
-
-### `exec`
-
-exec(query: , ...bindings: ):
-
-#### Parameters
-
-* `query`:
- * The SQL query string to be executed. `query` can contain `?` placeholders for parameter bindings. Multiple SQL statements, separated with a semicolon, can be executed in the `query`. With multiple SQL statements, any parameter bindings are applied to the last SQL statement in the `query`, and the returned cursor is only for the last SQL statement.
-* `...bindings`:
- * Optional variable number of arguments that correspond to the `?` placeholders in `query`.
-
-#### Returns
-
-A cursor (`SqlStorageCursor`) to iterate over query row results as objects. `SqlStorageCursor` is a JavaScript [Iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol), which supports iteration using `for (let row of cursor)`. `SqlStorageCursor` is also a JavaScript [Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterator_protocol), which supports iteration using `cursor.next()`.
-
-`SqlStorageCursor` supports the following methods:
-
-* `next()`
- * Returns an object representing the next value of the cursor. The returned object has `done` and `value` properties adhering to the JavaScript [Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterator_protocol). `done` is set to `false` when a next value is present, and `value` is set to the next row object in the query result. `done` is set to `true` when the entire cursor is consumed, and no `value` is set.
-* `toArray()`
- * Iterates through remaining cursor value(s) and returns an array of returned row objects.
-* `one()`
- * Returns a row object if query result has exactly one row. If query result has zero rows or more than one row, `one()` throws an exception.
-* `raw()`:
- * Returns an Iterator over the same query results, with each row as an array of column values (with no column names) rather than an object.
- * Returned Iterator supports `next()`, `toArray()`, and `one()` methods above.
- * Returned cursor and `raw()` iterator iterate over the same query results and can be combined. For example:
-
-```ts
-let cursor = this.sql.exec("SELECT * FROM artist ORDER BY artistname ASC;");
-let rawResult = cursor.raw().next();
-
-if (!rawResult.done) {
- console.log(rawResult.value); // prints [ 123, 'Alice' ]
-} else {
- // query returned zero results
-}
-
-console.log(cursor.toArray()); // prints [{ artistid: 456, artistname: 'Bob' },{ artistid: 789, artistname: 'Charlie' }]
-```
-
-`SqlStorageCursor` had the following properties:
-
-* `columnNames`:
- * The column names of the query in the order they appear in each row array returned by the `raw` iterator.
-* `rowsRead`:
- * The number of rows read so far as part of this SQL `query`. This may increase as you iterate the cursor. The final value is used for [SQL billing](/durable-objects/platform/pricing/#sqlite-storage-backend).
-* `rowsWritten`:
- * The number of rows written so far as part of this SQL `query`. This may increase as you iterate the cursor. The final value is used for [SQL billing](/durable-objects/platform/pricing/#sqlite-storage-backend).
-
-Note that `sql.exec()` cannot execute transaction-related statements like `BEGIN TRANSACTION` or `SAVEPOINT`. Instead, use the [`ctx.storage.transaction()`](/durable-objects/api/storage-api/#transaction) or [`ctx.storage.transactionSync()`](/durable-objects/api/storage-api/#transactionsync) APIs to start a transaction, and then execute SQL queries in your callback.
-
-#### Examples
-
-
-
-### `databaseSize`
-
-`databaseSize`:
-
-#### Returns
-
-The current SQLite database size in bytes.
-
-```ts
-let size = ctx.storage.sql.databaseSize;
-```
-
-## Point in time recovery
-
-For [Durable Objects classes with SQL storage](/durable-objects/reference/durable-objects-migrations/#enable-sqlite-storage-backend-on-new-durable-object-class-migration), the following point-in-time-recovery (PITR) API methods are available to restore a Durable Object's embedded SQLite database to any point in time in the past 30 days. These methods apply to the entire SQLite database contents, including both the object's stored SQL data and stored key-value data using the key-value `put()` API. The PITR API is not supported in local development because a durable log of data changes is not stored locally.
-
-The PITR API represents points in times using 'bookmarks'. A bookmark is a mostly alphanumeric string like `0000007b-0000b26e-00001538-0c3e87bb37b3db5cc52eedb93cd3b96b`. Bookmarks are designed to be lexically comparable: a bookmark representing an earlier point in time compares less than one representing a later point, using regular string comparison.
-
-### `getCurrentBookmark`
-
-ctx.storage.getCurrentBookmark():
-
-* Returns a bookmark representing the current point in time in the object's history.
-
-### `getBookmarkForTime`
-
-ctx.storage.getBookmarkForTime(timestamp: ):
-
-* Returns a bookmark representing approximately the given point in time, which must be within the last 30 days. If the timestamp is represented as a number, it is converted to a date as if using `new Date(timestamp)`.
-
-### `onNextSessionRestoreBookmark`
-
-ctx.storage.onNextSessionRestoreBookmark(bookmark: ):
-
- * Configures the Durable Object so that the next time it restarts, it should restore its storage to exactly match what the storage contained at the given bookmark. After calling this, the application should typically invoke `ctx.abort()` to restart the Durable Object, thus completing the point-in-time recovery.
-
-This method returns a special bookmark representing the point in time immediately before the recovery takes place (even though that point in time is still technically in the future). Thus, after the recovery completes, it can be undone by performing a second recovery to this bookmark.
-
-
-```ts
-let now = new Date();
-// restore to 2 days ago
-let bookmark = ctx.storage.getBookmarkForTime(now - 2);
-ctx.storage.onNextSessionRestoreBookmark(bookmark);
-```
-
-## TypeScript and query results
-
-You can use TypeScript [type parameters](https://www.typescriptlang.org/docs/handbook/2/generics.html#working-with-generic-type-variables) to provide a type for your results, allowing you to benefit from type hints and checks when iterating over the results of a query.
-
-:::caution
-
-Providing a type parameter does _not_ validate that the query result matches your type definition. In TypeScript, properties (fields) that do not exist in your result type will be silently dropped.
-
-:::
-
-Your type must conform to the shape of a TypeScript [Record](https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type) type representing the name (`string`) of the column and the type of the column. The column type must be a valid `SqlStorageValue`: one of `ArrayBuffer | string | number | null`.
-
-For example,
-
-```ts
-type User = {
- id: string;
- name: string;
- email_address: string;
- version: number;
-}
-```
-
-This type can then be passed as the type parameter to a `sql.exec` call:
-
-```ts
-// The type parameter is passed between the "pointy brackets" before the function argument:
-const result = this.ctx.storage.sql.exec("SELECT id, name, email_address, version FROM users WHERE id = ?", user_id).one()
-// result will now have a type of "User"
-
-// Alternatively, if you are iterating over results using a cursor
-let cursor = this.sql.exec("SELECT id, name, email_address, version FROM users WHERE id = ?", user_id)
-for (let row of cursor) {
- // Each row object will be of type User
-}
-
-// Or, if you are using raw() to convert results into an array, define an array type:
-type UserRow = [
- id: string,
- name: string,
- email_address: string,
- version: number,
-];
-
-// ... and then pass it as the type argument to the raw() method:
-let cursor = sql.exec("SELECT id, name, email_address, version FROM users WHERE id = ?", user_id).raw();
-
-for (let row of cursor) {
- // row is of type User
-}
-```
-
-You can represent the shape of any result type you wish, including more complex types. If you are performing a JOIN across multiple tables, you can compose a type that reflects the results of your queries.
diff --git a/src/content/docs/durable-objects/api/storage-api.mdx b/src/content/docs/durable-objects/api/storage-api.mdx
index a20ac57ceb42b88..7523f06c51439be 100644
--- a/src/content/docs/durable-objects/api/storage-api.mdx
+++ b/src/content/docs/durable-objects/api/storage-api.mdx
@@ -11,22 +11,41 @@ import {
MetaInfo,
GlossaryTooltip,
TypeScriptExample,
+ Details,
} from "~/components";
The Durable Object Storage API allows Durable Objects to access transactional and strongly consistent storage. A Durable Object's attached storage is private to its unique instance and cannot be accessed by other objects.
-:::note[Scope of Durable Object storage]
-Note that Durable Object storage is scoped by individual Durable Objects.
+The Durable Object Storage API comes with several methods, including SQL, point-in-time recovery (PITR), key-value (KV), and alarm APIs. Available API methods depend on the storage backend for a Durable Objects class, either [SQLite](/durable-objects/best-practices/access-durable-objects-storage/#create-sqlite-backed-durable-object-class) or [KV](/durable-objects/reference/durable-objects-migrations/#create-durable-object-class-with-key-value-storage).
-- An account can have many Durable Object namespaces.
-- A namespace can have many Durable Objects.
+:::note[Recommended SQLite-backed Durable Objects]
+Cloudflare recommends all new Durable Object classes use the [SQLite storage backend](/durable-objects/best-practices/access-durable-objects-storage/#create-sqlite-backed-durable-object-class).
-However, storage is scoped per individual Durable Object.
+The [key-value storage backend](/durable-objects/reference/durable-objects-migrations/#create-durable-object-class-with-key-value-storage) remains for backwards compatibility, and a migration path from KV storage to SQLite storage for existing Durable Object classes will be available in the future.
:::
-Durable Objects gain access to a persistent Durable Object Storage API via the `DurableObjectStorage` interface and accessed by the `DurableObjectState::storage` property. This is frequently accessed via `this.ctx.storage` when the `ctx` parameter passed to the Durable Object constructor.
+
-JavaScript is a single-threaded and event-driven programming language. This means that JavaScript runtimes, by default, allow requests to interleave with each other which can lead to concurrency bugs. The Durable Objects runtime uses a combination of input gates and output gates to avoid this type of concurrency bug when performing storage operations.
+| Methods 1 | SQLite-backed Durable Object class | KV-backed Durable Object class |
+| ----------------------- | ---------------------------- | ------------------------ |
+| SQL API | ✅ | ❌ |
+| PITR API | ✅ | ❌ |
+| KV API | ✅ 2, 3 | ✅ |
+| Alarms API | ✅ | ✅ |
+
+
+
+1 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.
+
+2 KV API methods like `get()`, `put()`, `delete()`, or `list()` store data in a hidden SQLite table.
+
+3 KV methods which were previously asynchronous with KV storage (for example, [`get`](/durable-objects/api/storage-api/#get), [`put`](/durable-objects/api/storage-api/#put), [`delete`](/durable-objects/api/storage-api/#delete), [`deleteAll`](/durable-objects/api/storage-api/#deleteall), [`list`](/durable-objects/api/storage-api/#list)) are synchronous, even though they return promises. These methods will have completed their operations before they return the promise.
+
+
+
+## Access storage
+
+Durable Objects gain access to Storage API via the `DurableObjectStorage` interface and accessed by the `DurableObjectState::storage` property. This is frequently accessed via `this.ctx.storage` with the `ctx` parameter passed to the Durable Object constructor.
The following code snippet shows you how to store and retrieve data using the Durable Object Storage API.
@@ -45,24 +64,147 @@ export class Counter extends DurableObject {
}
}
-
```
-## Methods
+JavaScript is a single-threaded and event-driven programming language. This means that JavaScript runtimes, by default, allow requests to interleave with each other which can lead to concurrency bugs. The Durable Objects runtime uses a combination of input gates and output gates to avoid this type of concurrency bug when performing storage operations. Learn more in our [blog post](https://blog.cloudflare.com/durable-objects-easy-fast-correct-choose-three/).
+
+## SQL API
+
+The `SqlStorage` interface encapsulates methods that modify the SQLite database embedded within a Durable Object. The `SqlStorage` interface is accessible via the [`sql` property](/durable-objects/api/storage-api/#sql) of `DurableObjectStorage` class.
+
+For example, using `sql.exec()`, a user can create a table, then insert rows into the table.
+
+```ts
+import { DurableObject } from "cloudflare:workers";
+
+export class MyDurableObject extends DurableObject {
+ sql: SqlStorage;
+ constructor(ctx: DurableObjectState, env: Env) {
+ super(ctx, env);
+ this.sql = ctx.storage.sql;
+
+ this.sql.exec(`CREATE TABLE IF NOT EXISTS artist(
+ artistid INTEGER PRIMARY KEY,
+ artistname TEXT
+ );INSERT INTO artist (artistid, artistname) VALUES
+ (123, 'Alice'),
+ (456, 'Bob'),
+ (789, 'Charlie');`
+ );
+ }
+}
+```
+
+* SQL API methods accessed with `ctx.storage.sql` are only allowed on [Durable Object classes with SQLite storage backend](/durable-objects/best-practices/access-durable-objects-storage/#create-sqlite-backed-durable-object-class) and will return an error if called on Durable Object classes with a key-value storage backend.
+* When writing data, every index counts as an additional row. However, indexes may be beneficial for read-heavy use cases. Refer to [Index for SQLite Durable Objects](/durable-objects/best-practices/access-durable-objects-storage/#index-for-sqlite-durable-objects).
+* Writing data to [SQLite virtual tables](https://www.sqlite.org/vtab.html) also counts towards rows written.
-:::note[SQLite in Durable Objects Beta]
+### `exec`
-The new beta version of Durable Objects is available where each Durable Object has a private, embedded SQLite database. When deploying a new Durable Object class, users can [opt-in to a SQLite storage backend](/durable-objects/best-practices/access-durable-objects-storage/#sqlite-storage-backend) to access the new [SQL API](/durable-objects/api/sql-storage/#exec). Otherwise, a Durable Object class has a key-value storage backend.
+exec(query: , ...bindings: ):
+#### Parameters
+
+* `query`:
+ * The SQL query string to be executed. `query` can contain `?` placeholders for parameter bindings. Multiple SQL statements, separated with a semicolon, can be executed in the `query`. With multiple SQL statements, any parameter bindings are applied to the last SQL statement in the `query`, and the returned cursor is only for the last SQL statement.
+* `...bindings`:
+ * Optional variable number of arguments that correspond to the `?` placeholders in `query`.
+
+#### Returns
+
+A cursor (`SqlStorageCursor`) to iterate over query row results as objects. `SqlStorageCursor` is a JavaScript [Iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol), which supports iteration using `for (let row of cursor)`. `SqlStorageCursor` is also a JavaScript [Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterator_protocol), which supports iteration using `cursor.next()`.
+
+`SqlStorageCursor` supports the following methods:
+
+* `next()`
+ * Returns an object representing the next value of the cursor. The returned object has `done` and `value` properties adhering to the JavaScript [Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterator_protocol). `done` is set to `false` when a next value is present, and `value` is set to the next row object in the query result. `done` is set to `true` when the entire cursor is consumed, and no `value` is set.
+* `toArray()`
+ * Iterates through remaining cursor value(s) and returns an array of returned row objects.
+* `one()`
+ * Returns a row object if query result has exactly one row. If query result has zero rows or more than one row, `one()` throws an exception.
+* `raw()`:
+ * Returns an Iterator over the same query results, with each row as an array of column values (with no column names) rather than an object.
+ * Returned Iterator supports `next()`, `toArray()`, and `one()` methods above.
+ * Returned cursor and `raw()` iterator iterate over the same query results and can be combined. For example:
+
+```ts
+let cursor = this.sql.exec("SELECT * FROM artist ORDER BY artistname ASC;");
+let rawResult = cursor.raw().next();
+
+if (!rawResult.done) {
+ console.log(rawResult.value); // prints [ 123, 'Alice' ]
+} else {
+ // query returned zero results
+}
+
+console.log(cursor.toArray()); // prints [{ artistid: 456, artistname: 'Bob' },{ artistid: 789, artistname: 'Charlie' }]
+```
+
+`SqlStorageCursor` has the following properties:
+
+* `columnNames`:
+ * The column names of the query in the order they appear in each row array returned by the `raw` iterator.
+* `rowsRead`:
+ * The number of rows read so far as part of this SQL `query`. This may increase as you iterate the cursor. The final value is used for [SQL billing](/durable-objects/platform/pricing/#sqlite-storage-backend).
+* `rowsWritten`:
+ * The number of rows written so far as part of this SQL `query`. This may increase as you iterate the cursor. The final value is used for [SQL billing](/durable-objects/platform/pricing/#sqlite-storage-backend).
+
+:::note[SQL transactions]
+Note that `sql.exec()` cannot execute transaction-related statements like `BEGIN TRANSACTION` or `SAVEPOINT`. Instead, use the [`ctx.storage.transaction()`](/durable-objects/api/storage-api/#transaction) or [`ctx.storage.transactionSync()`](/durable-objects/api/storage-api/#transactionsync) APIs to start a transaction, and then execute SQL queries in your callback.
:::
-The Durable Object Storage API comes with several methods, including key-value (KV) API, SQL API, and point-in-time-recovery (PITR) API.
+#### Examples
+
+
+
+### `databaseSize`
-- Durable Object classes with the default, key-value storage backend can use KV API.
-- Durable Object classes with the [SQLite storage backend](/durable-objects/best-practices/access-durable-objects-storage/#sqlite-storage-backend) can use KV API, SQL API, and PITR API. KV API methods like `get()`, `put()`, `delete()`, or `list()` store data in a hidden SQLite table.
+`databaseSize`:
-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.
+#### Returns
+
+The current SQLite database size in bytes.
+
+```ts
+let size = ctx.storage.sql.databaseSize;
+```
+
+## PITR (Point In Time Recovery) API
+
+For [SQLite-backed Durable Objects](/durable-objects/best-practices/access-durable-objects-storage/#create-sqlite-backed-durable-object-class), the following point-in-time-recovery (PITR) API methods are available to restore a Durable Object's embedded SQLite database to any point in time in the past 30 days. These methods apply to the entire SQLite database contents, including both the object's stored SQL data and stored key-value data using the key-value `put()` API. The PITR API is not supported in local development because a durable log of data changes is not stored locally.
+
+The PITR API represents points in time using 'bookmarks'. A bookmark is a mostly alphanumeric string like `0000007b-0000b26e-00001538-0c3e87bb37b3db5cc52eedb93cd3b96b`. Bookmarks are designed to be lexically comparable: a bookmark representing an earlier point in time compares less than one representing a later point, using regular string comparison.
+
+### `getCurrentBookmark`
+
+ctx.storage.getCurrentBookmark():
+
+* Returns a bookmark representing the current point in time in the object's history.
+
+### `getBookmarkForTime`
+
+ctx.storage.getBookmarkForTime(timestamp: ):
+
+* Returns a bookmark representing approximately the given point in time, which must be within the last 30 days. If the timestamp is represented as a number, it is converted to a date as if using `new Date(timestamp)`.
+
+### `onNextSessionRestoreBookmark`
+
+ctx.storage.onNextSessionRestoreBookmark(bookmark: ):
+
+ * Configures the Durable Object so that the next time it restarts, it should restore its storage to exactly match what the storage contained at the given bookmark. After calling this, the application should typically invoke `ctx.abort()` to restart the Durable Object, thus completing the point-in-time recovery.
+
+This method returns a special bookmark representing the point in time immediately before the recovery takes place (even though that point in time is still technically in the future). Thus, after the recovery completes, it can be undone by performing a second recovery to this bookmark.
+
+
+```ts
+let now = new Date();
+// restore to 2 days ago
+let bookmark = ctx.storage.getBookmarkForTime(now - 2);
+ctx.storage.onNextSessionRestoreBookmark(bookmark);
+```
+
+## KV API
### `get`
@@ -106,14 +248,6 @@ Each method is implicitly wrapped inside a transaction, such that its results ar
- 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.
-### `deleteAll`
-
-- deleteAll(options ):
-
- - Deletes all stored data, effectively deallocating all storage used by the Durable Object. For Durable Objects with a key-value storage backend, `deleteAll()` removes all keys and associated values for an individual Durable Object. For Durable Objects with a [SQLite storage backend](/durable-objects/best-practices/access-durable-objects-storage/#sqlite-storage-backend), `deleteAll()` removes the entire contents of a Durable Object's private SQLite database, including both SQL data and key-value data.
- - For Durable Objects with a key-value storage backend, an in-progress `deleteAll()` operation can fail, which may leave a subset of data undeleted. Durable Objects with a SQLite storage backend do not have a partial `deleteAll()` issue because `deleteAll()` operations are atomic (all or nothing).
- - `deleteAll()` does not proactively delete [Alarms](/durable-objects/api/alarms/). Use [`deleteAlarm()`](/durable-objects/api/alarms/#deletealarm) to delete an alarm.
-
#### Supported options
- `put()`, `delete()` and `deleteAll()` support the following options:
@@ -191,76 +325,89 @@ The `put()` method returns a `Promise`, but most applications can discard this p
- Same as the option to `get()`, above.
-### `transaction`
+## Alarms
-- `transaction(closureFunction(txn))`:
+### `getAlarm`
- - Runs the sequence of storage operations called on `txn` in a single transaction that either commits successfully or aborts.
+- getAlarm(options ):
- - Explicit transactions are no longer necessary. Any series of write operations with no intervening `await` will automatically be submitted atomically, and the system will prevent concurrent events from executing while `await` a read operation (unless you use `allowConcurrency: true`). Therefore, a series of reads followed by a series of writes (with no other intervening I/O) are automatically atomic and behave like a transaction.
+ - 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`.
-- `txn`
+#### Supported options
- - 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 `ctx.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.
+- Same options as [`get()`](/durable-objects/api/storage-api/#get), but without `noCache`.
- * When using [the SQLite-backed storage engine](/durable-objects/best-practices/access-durable-objects-storage/#sqlite-storage-backend), the `txn` object is obsolete. Any storage operations performed directly on the `ctx.storage` object, including SQL queries using [`ctx.storage.sql.exec()`](/durable-objects/api/sql-storage/#exec), will be considered part of the transaction.
+### `setAlarm`
-### `transactionSync`
+- setAlarm(scheduledTime , options{" "} ):
-- `transactionSync(callback)`:
+ - Sets the current alarm time, accepting either a JavaScript `Date`, or integer milliseconds since epoch.
- - Only available when using [the SQLite-backed storage engine](/durable-objects/best-practices/access-durable-objects-storage/#sqlite-storage-backend).
+ 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.
- - Invokes `callback()` wrapped in a transaction, and returns its result.
+### `deleteAlarm`
- - If `callback()` throws an exception, the transaction will be rolled back.
+- deleteAlarm(options ):
- * The callback must complete synchronously, that is, it should not be declared `async` nor otherwise return a Promise. Only synchronous storage operations can be part of the transaction. This is intended for use with SQL queries using [`ctx.storage.sql.exec()`](/durable-objects/api/sql-storage/#exec), which complete sychronously.
+ - Deletes the alarm if one exists. Does not cancel the alarm handler if it is currently executing.
-### `sync`
+#### Supported options
-- `sync()`:
+- `setAlarm()` and `deleteAlarm()` support the same options as [`put()`](/durable-objects/api/storage-api/#put), but without `noCache`.
- - Synchronizes any pending writes to disk.
+## Other
- - This is similar to normal behavior from automatic write coalescing. If there are any pending writes in the write buffer (including those submitted with [the `allowUnconfirmed` option](/durable-objects/api/storage-api/#supported-options-1)), the returned promise will resolve when they complete. If there are no pending writes, the returned promise will be already resolved.
+### `deleteAll`
-### `getAlarm`
+- deleteAll(options ):
-- getAlarm(options ):
+ - Deletes all stored data, effectively deallocating all storage used by the Durable Object. For Durable Objects with a key-value storage backend, `deleteAll()` removes all keys and associated values for an individual Durable Object. For Durable Objects with a [SQLite storage backend](/durable-objects/best-practices/access-durable-objects-storage/#create-sqlite-backed-durable-object-class), `deleteAll()` removes the entire contents of a Durable Object's private SQLite database, including both SQL data and key-value data.
+ - For Durable Objects with a key-value storage backend, an in-progress `deleteAll()` operation can fail, which may leave a subset of data undeleted. Durable Objects with a SQLite storage backend do not have a partial `deleteAll()` issue because `deleteAll()` operations are atomic (all or nothing).
+ - `deleteAll()` does not proactively delete [alarms](/durable-objects/api/alarms/). Use [`deleteAlarm()`](/durable-objects/api/alarms/#deletealarm) to delete an alarm.
- - 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`.
+### `transactionSync`
-#### Supported options
+- `transactionSync(callback)`:
-- Same options as [`get()`](/durable-objects/api/storage-api/#get), but without `noCache`.
+ - Only available when using SQLite-backed Durable Objects.
-### `setAlarm`
+ - Invokes `callback()` wrapped in a transaction, and returns its result.
-- setAlarm(scheduledTime , options{" "} ):
+ - If `callback()` throws an exception, the transaction will be rolled back.
- - Sets the current alarm time, accepting either a JavaScript `Date`, or integer milliseconds since epoch.
+ * The callback must complete synchronously, that is, it should not be declared `async` nor otherwise return a Promise. Only synchronous storage operations can be part of the transaction. This is intended for use with SQL queries using [`ctx.storage.sql.exec()`](/durable-objects/api/storage-api/#exec), which complete sychronously.
- 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.
+### `transaction`
-### `deleteAlarm`
+- `transaction(closureFunction(txn))`:
-- deleteAlarm(options ):
+ - Runs the sequence of storage operations called on `txn` in a single transaction that either commits successfully or aborts.
- - Deletes the alarm if one exists. Does not cancel the alarm handler if it is currently executing.
+ - Explicit transactions are no longer necessary. Any series of write operations with no intervening `await` will automatically be submitted atomically, and the system will prevent concurrent events from executing while `await` a read operation (unless you use `allowConcurrency: true`). Therefore, a series of reads followed by a series of writes (with no other intervening I/O) are automatically atomic and behave like a transaction.
-#### Supported options
+- `txn`
-- `setAlarm()` and `deleteAlarm()` support the same options as [`put()`](/durable-objects/api/storage-api/#put), but without `noCache`.
+ - 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 `ctx.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.
-## Properties
+ - When using [the SQLite-backed storage engine](/durable-objects/best-practices/access-durable-objects-storage/#sqlite-storage-backend), the `txn` object is obsolete. Any storage operations performed directly on the `ctx.storage` object, including SQL queries using [`ctx.storage.sql.exec()`](/durable-objects/api/storage-api/#exec), will be considered part of the transaction.
+
+### `sync`
+
+- `sync()`:
+
+ - 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 [the `allowUnconfirmed` option](/durable-objects/api/storage-api/#supported-options-1)), the returned promise will resolve when they complete. If there are no pending writes, the returned promise will be already resolved.
+
+## Storage properties
### `sql`
-`sql` is a readonly property of type `DurableObjectStorage` encapsulating the [SQL API](/durable-objects/api/sql-storage/).
+`sql` is a readonly property of type `DurableObjectStorage` encapsulating the [SQL API](/durable-objects/api/storage-api/#sql-api).
## Related resources
- [Durable Objects: Easy, Fast, Correct – Choose Three](https://blog.cloudflare.com/durable-objects-easy-fast-correct-choose-three/)
+- [Zero-latency SQLite storage in every Durable Object blog](https://blog.cloudflare.com/sqlite-in-durable-objects/)
- [WebSockets API](/durable-objects/best-practices/websockets/)
-```
+
diff --git a/src/content/docs/durable-objects/best-practices/access-durable-objects-storage.mdx b/src/content/docs/durable-objects/best-practices/access-durable-objects-storage.mdx
index e291ac0d8e2dda0..118b39c53fcb461 100644
--- a/src/content/docs/durable-objects/best-practices/access-durable-objects-storage.mdx
+++ b/src/content/docs/durable-objects/best-practices/access-durable-objects-storage.mdx
@@ -2,21 +2,49 @@
title: Access Durable Objects Storage
pcx_content_type: concept
sidebar:
- order: 4
+ order: 3
---
import { Render, GlossaryTooltip, WranglerConfig } from "~/components";
-Durable Objects are a powerful compute API that provides a compute with storage building block. Each Durable Object has its own private, transactional and strongly consistent storage. Durable Objects Storage API provides access to a Durable Object's attached storage.
+Durable Objects are a powerful compute API that provides a compute with storage building block. Each Durable Object has its own private, transactional, and strongly consistent storage. Durable Objects Storage API provides access to a Durable Object's attached storage.
A Durable Object's [in-memory state](/durable-objects/reference/in-memory-state/) is preserved as long as the Durable Object is not evicted from memory. Inactive Durable Objects with no incoming request traffic can be evicted. There are normal operations like [code deployments](/workers/configuration/versions-and-deployments/) that trigger Durable Objects to restart and lose their in-memory state. For these reasons, you should use Storage API to persist state durably on disk that needs to survive eviction or restart of Durable Objects.
## Access storage
-By default, a Durable Object class leverages a key-value storage backend. New Durable Object classes can opt-in to using a [SQLite storage backend](/durable-objects/best-practices/access-durable-objects-storage/#sqlite-storage-backend).
+:::note[Recommended SQLite-backed Durable Objects]
+Cloudflare recommends all new Durable Object classes use the [SQLite storage backend](/durable-objects/best-practices/access-durable-objects-storage/#create-sqlite-backed-durable-object-class).
-[Storage API methods](/durable-objects/api/storage-api/#methods) are available on `ctx.storage` parameter passed to the Durable Object constructor. Storage API has key-value APIs and SQL APIs. Only Durable Object classes with a SQLite storage backend can access SQL API.
+The [key-value storage backend](/durable-objects/reference/durable-objects-migrations/#create-durable-object-class-with-key-value-storage) remains for backwards compatibility, and a migration path from KV storage to SQLite storage for existing Durable Object classes will be available in the future.
+:::
+
+
+
+[Storage API methods](/durable-objects/api/storage-api/#methods) are available on `ctx.storage` parameter passed to the Durable Object constructor. Storage API has several methods, including SQL, point-in-time recovery (PITR), key-value (KV), and alarm APIs.
+
+Only Durable Object classes with a SQLite storage backend can access SQL API.
+
+### Create SQLite-backed Durable Object class
+
+Use `new_sqlite_classes` on the migration in your Worker's Wrangler file:
+
+
+
+```toml
+[[migrations]]
+tag = "v1" # Should be unique for each entry
+new_sqlite_classes = ["MyDurableObject"] # Array of new classes
+```
+
+
+
+[SQL API](/durable-objects/api/storage-api/#exec) is available on `ctx.storage.sql` parameter passed to the Durable Object constructor.
+
+SQLite-backed Durable Objects also offer [point-in-time recovery API](/durable-objects/api/storage-api/#pitr-point-in-time-recovery-api), which uses bookmarks to allow you to restore a Durable Object's embedded SQLite database to any point in time in the past 30 days.
+
+### Initialize instance variables from storage
A common pattern is to initialize a Durable Object from [persistent storage](/durable-objects/api/storage-api/) and set instance variables the first time it is accessed. Since future accesses are routed to the same Durable Object, it is then possible to return any initialized values without making further calls to persistent storage.
@@ -42,44 +70,97 @@ export class Counter extends DurableObject {
}
}
```
-### Removing a Durable Object's storage
+
+### Remove a Durable Object's storage
A Durable Object fully ceases to exist if, when it shuts down, its storage is empty. If you never write to a Durable Object's storage at all (including setting alarms), then storage remains empty, and so the Durable Object will no longer exist once it shuts down.
-However if you ever write using [Storage API](/durable-objects/api/storage-api/), including setting alarms, then you must explicitly call [`storage.deleteAll()`](/durable-objects/api/storage-api/#deleteall) to empty storage. It is not sufficient to simply delete the specific data that you wrote, such as deleting a key or dropping a table, as some metadata may remain. The only way to remove all storage is to call `deleteAll()`. Calling `deleteAll()` ensures that a Durable Object will not be billed for storage.
+However if you ever write using [Storage API](/durable-objects/api/storage-api/), including setting alarms, then you must explicitly call [`storage.deleteAll()`](/durable-objects/api/storage-api/#deleteall) to empty storage and [`storage.deleteAlarm()`](/durable-objects/api/storage-api/#deletealarm) if you've configured an alarm. It is not sufficient to simply delete the specific data that you wrote, such as deleting a key or dropping a table, as some metadata may remain. The only way to remove all storage is to call `deleteAll()`. Calling `deleteAll()` ensures that a Durable Object will not be billed for storage.
-## SQLite storage backend
+```ts
+export class MyDurableObject extends DurableObject {
-:::note[SQLite in Durable Objects Beta]
+ constructor(ctx: DurableObjectState, env: Env) {
+ super(ctx, env);
+ }
-The new beta version of Durable Objects is available where each Durable Object has a private, embedded SQLite database. When deploying a new Durable Object class, users can opt-in to a SQLite storage backend in order to access new [SQL API](/durable-objects/api/sql-storage/#exec). Otherwise, a Durable Object class has a key-value storage backend.
+ // Clears Durable Object storage
+ async clearDo():Promise {
+ // If you've configured a Durable Object alarm
+ await this.ctx.storage.deleteAlarm();
+
+ // This will delete all the storage associated with this Durable Object instance
+ // This will also delete the Durable Object instance itself
+ await this.ctx.storage.deleteAll();
+ }
+}
+```
+
+## SQL API Examples
+
+
+
+## TypeScript and query results
+
+You can use TypeScript [type parameters](https://www.typescriptlang.org/docs/handbook/2/generics.html#working-with-generic-type-variables) to provide a type for your results, allowing you to benefit from type hints and checks when iterating over the results of a query.
+
+:::caution
+
+Providing a type parameter does _not_ validate that the query result matches your type definition. In TypeScript, properties (fields) that do not exist in your result type will be silently dropped.
:::
-To allow a new Durable Object class to use SQLite storage backend, use `new_sqlite_classes` on the migration in your Worker's Wrangler file:
+Your type must conform to the shape of a TypeScript [Record](https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type) type representing the name (`string`) of the column and the type of the column. The column type must be a valid `SqlStorageValue`: one of `ArrayBuffer | string | number | null`.
-
+For example,
-```toml
-[[migrations]]
-tag = "v1" # Should be unique for each entry
-new_sqlite_classes = ["MyDurableObject"] # Array of new classes
+```ts
+type User = {
+ id: string;
+ name: string;
+ email_address: string;
+ version: number;
+}
```
-
+This type can then be passed as the type parameter to a `sql.exec()` call:
-[SQL API](/durable-objects/api/sql-storage/#exec) is available on `ctx.storage.sql` parameter passed to the Durable Object constructor.
+```ts
+// The type parameter is passed between angle brackets before the function argument:
+const result = this.ctx.storage.sql.exec("SELECT id, name, email_address, version FROM users WHERE id = ?", user_id).one()
+// result will now have a type of "User"
+
+// Alternatively, if you are iterating over results using a cursor
+let cursor = this.sql.exec("SELECT id, name, email_address, version FROM users WHERE id = ?", user_id)
+for (let row of cursor) {
+ // Each row object will be of type User
+}
-### Examples
+// Or, if you are using raw() to convert results into an array, define an array type:
+type UserRow = [
+ id: string,
+ name: string,
+ email_address: string,
+ version: number,
+];
-
+// ... and then pass it as the type argument to the raw() method:
+let cursor = sql.exec("SELECT id, name, email_address, version FROM users WHERE id = ?", user_id).raw();
-
+for (let row of cursor) {
+ // row is of type User
+}
+```
-## Index for SQLite Durable Objects
+You can represent the shape of any result type you wish, including more complex types. If you are performing a
+`JOIN` across multiple tables, you can compose a type that reflects the results of your queries.
+
+## Indexes in SQLite
Creating indexes for your most queried tables and filtered columns reduces how much data is scanned and improves query performance at the same time. If you have a read-heavy workload (most common), this can be particularly advantageous. Writing to columns referenced in an index will add at least one (1) additional row written to account for updating the index, but this is typically offset by the reduction in rows read due to the benefits of an index.
+
+
## Related resources
* [Zero-latency SQLite storage in every Durable Object blog post](https://blog.cloudflare.com/sqlite-in-durable-objects)
diff --git a/src/content/docs/durable-objects/best-practices/create-durable-object-stubs-and-send-requests.mdx b/src/content/docs/durable-objects/best-practices/create-durable-object-stubs-and-send-requests.mdx
index b88268d27087972..b8ce59310852676 100644
--- a/src/content/docs/durable-objects/best-practices/create-durable-object-stubs-and-send-requests.mdx
+++ b/src/content/docs/durable-objects/best-practices/create-durable-object-stubs-and-send-requests.mdx
@@ -1,5 +1,5 @@
---
-title: Invoking methods
+title: Invoke methods
pcx_content_type: concept
sidebar:
order: 2
diff --git a/src/content/docs/durable-objects/best-practices/error-handling.mdx b/src/content/docs/durable-objects/best-practices/error-handling.mdx
index abcbaf755dc3d10..1bd32bc372a8142 100644
--- a/src/content/docs/durable-objects/best-practices/error-handling.mdx
+++ b/src/content/docs/durable-objects/best-practices/error-handling.mdx
@@ -2,7 +2,7 @@
title: Error handling
pcx_content_type: concept
sidebar:
- order: 4
+ order: 6
---
import { GlossaryTooltip } from "~/components";
diff --git a/src/content/docs/durable-objects/best-practices/websockets.mdx b/src/content/docs/durable-objects/best-practices/websockets.mdx
index d9c640931698c9f..7f0e18b0f4f5be5 100644
--- a/src/content/docs/durable-objects/best-practices/websockets.mdx
+++ b/src/content/docs/durable-objects/best-practices/websockets.mdx
@@ -1,5 +1,5 @@
---
-title: Using WebSockets
+title: Use WebSockets
pcx_content_type: concept
sidebar:
order: 5
@@ -9,12 +9,12 @@ import { Tabs, TabItem, GlossaryTooltip, Type } from "~/components";
This guide covers how to use Durable Objects as WebSocket servers that can connect thousands of clients (per instance), as well as a WebSocket client to connect to other servers or even Durable Objects.
-There are two sets of WebSockets API
+There are two sets of WebSockets API:
-1. Native Durable Object WebSocket API, which allows your Durable Object to hibernate without disconnecting clients when not actively doing work **(recommended)**
+1. Native Durable Object WebSocket API, which allows your Durable Object to hibernate without disconnecting clients when not actively doing work **(recommended)**.
2. Web Standard WebSocket APIs, using the familiar `addEventListener` event pattern.
-### What are WebSockets?
+### What are WebSockets?
WebSockets are long-lived TCP connections that enable bi-directional, real-time communication between client and server. Both Cloudflare Durable Objects and Workers can act as WebSocket endpoints – either as a client or as a server. Because WebSocket sessions are long-lived, applications commonly use Durable Objects to accept either the client or server connection. While there are other use cases for using Workers exclusively with WebSockets, for example proxying WebSocket messages, WebSockets are most useful when combined with Durable Objects.
@@ -138,7 +138,7 @@ export class WebSocketHibernationServer extends DurableObject {
-Similar to the WebSocket Standard API example, to execute this code, configure your Wrangler file to include a Durable Object [binding](/durable-objects/get-started/tutorial/#5-configure-durable-object-bindings) and [migration](/durable-objects/reference/durable-objects-migrations/) based on the namespace and class name chosen previously.
+Similar to the WebSocket Standard API example, to execute this code, configure your Wrangler file to include a Durable Object [binding](/durable-objects/get-started/#4-configure-durable-object-bindings) and [migration](/durable-objects/reference/durable-objects-migrations/) based on the namespace and class name chosen previously.
```toml title="wrangler.toml"
name = "websocket-hibernation-server"
@@ -149,7 +149,7 @@ class_name = "WebSocketHibernationServer"
[[migrations]]
tag = "v1"
-new_classes = ["WebSocketHibernationServer"]
+new_sqlite_classes = ["WebSocketHibernationServer"]
```
A full example can be found in [Build a WebSocket server with WebSocket Hibernation](/durable-objects/examples/websocket-hibernation-server/).
@@ -379,7 +379,7 @@ export class WebSocketServer extends DurableObject {
-To execute this code, configure your Wrangler file to include a Durable Object [binding](/durable-objects/get-started/tutorial/#5-configure-durable-object-bindings) and [migration](/durable-objects/reference/durable-objects-migrations/) based on the namespace and class name chosen previously.
+To execute this code, configure your Wrangler file to include a Durable Object [binding](/durable-objects/get-started/#4-configure-durable-object-bindings) and [migration](/durable-objects/reference/durable-objects-migrations/) based on the namespace and class name chosen previously.
```toml title="wrangler.toml"
name = "websocket-server"
@@ -390,7 +390,7 @@ class_name = "WebSocketServer"
[[migrations]]
tag = "v1"
-new_classes = ["WebSocketServer"]
+new_sqlite_classes = ["WebSocketServer"]
```
A full example can be found in [Build a WebSocket server](/durable-objects/examples/websocket-server/).
diff --git a/src/content/docs/durable-objects/examples/agents.mdx b/src/content/docs/durable-objects/examples/agents.mdx
index 2ba4fdef824cfc4..ad650df4e5c97f0 100644
--- a/src/content/docs/durable-objects/examples/agents.mdx
+++ b/src/content/docs/durable-objects/examples/agents.mdx
@@ -3,7 +3,7 @@ pcx_content_type: navigation
title: Agents
external_link: /agents/
sidebar:
- order: 10
+ order: 99
head: []
description: Build AI-powered Agents on Cloudflare
---
\ No newline at end of file
diff --git a/src/content/docs/durable-objects/examples/alarms-api.mdx b/src/content/docs/durable-objects/examples/alarms-api.mdx
index 9327fb84179f23a..e0453f1d2359181 100644
--- a/src/content/docs/durable-objects/examples/alarms-api.mdx
+++ b/src/content/docs/durable-objects/examples/alarms-api.mdx
@@ -73,7 +73,7 @@ export class Batcher extends DurableObject {
The `alarm()` handler will be called once every 10 seconds. If an unexpected error terminates the Durable Object, the `alarm()` handler will be re-instantiated on another machine. Following a short delay, the `alarm()` handler will run from the beginning on the other machine.
-Finally, configure your Wrangler file to include a Durable Object [binding](/durable-objects/get-started/tutorial/#5-configure-durable-object-bindings) and [migration](/durable-objects/reference/durable-objects-migrations/) based on the namespace and class name chosen previously.
+Finally, configure your Wrangler file to include a Durable Object [binding](/durable-objects/get-started/#4-configure-durable-object-bindings) and [migration](/durable-objects/reference/durable-objects-migrations/) based on the namespace and class name chosen previously.
@@ -86,7 +86,7 @@ class_name = "Batcher"
[[migrations]]
tag = "v1"
-new_classes = ["Batcher"]
+new_sqlite_classes = ["Batcher"]
```
diff --git a/src/content/docs/durable-objects/examples/build-a-counter.mdx b/src/content/docs/durable-objects/examples/build-a-counter.mdx
index ec840c752b2577f..bea9ba2c49ee45b 100644
--- a/src/content/docs/durable-objects/examples/build-a-counter.mdx
+++ b/src/content/docs/durable-objects/examples/build-a-counter.mdx
@@ -165,7 +165,7 @@ export class Counter extends DurableObject {
-Finally, configure your Wrangler file to include a Durable Object [binding](/durable-objects/get-started/tutorial/#5-configure-durable-object-bindings) and [migration](/durable-objects/reference/durable-objects-migrations/) based on the namespace and class name chosen previously.
+Finally, configure your Wrangler file to include a Durable Object [binding](/durable-objects/get-started/#4-configure-durable-object-bindings) and [migration](/durable-objects/reference/durable-objects-migrations/) based on the namespace and class name chosen previously.
@@ -178,7 +178,7 @@ class_name = "Counter"
[[migrations]]
tag = "v1"
-new_classes = ["Counter"]
+new_sqlite_classes = ["Counter"]
```
diff --git a/src/content/docs/durable-objects/examples/build-a-rate-limiter.mdx b/src/content/docs/durable-objects/examples/build-a-rate-limiter.mdx
index 7e0f3273ad12278..400b7ab0e3cbf85 100644
--- a/src/content/docs/durable-objects/examples/build-a-rate-limiter.mdx
+++ b/src/content/docs/durable-objects/examples/build-a-rate-limiter.mdx
@@ -277,7 +277,7 @@ export class RateLimiter extends DurableObject {
-Finally, configure your Wrangler file to include a Durable Object [binding](/durable-objects/get-started/tutorial/#5-configure-durable-object-bindings) and [migration](/durable-objects/reference/durable-objects-migrations/) based on the namespace and class name chosen previously.
+Finally, configure your Wrangler file to include a Durable Object [binding](/durable-objects/get-started/#4-configure-durable-object-bindings) and [migration](/durable-objects/reference/durable-objects-migrations/) based on the namespace and class name chosen previously.
@@ -290,7 +290,7 @@ class_name = "RateLimiter"
[[migrations]]
tag = "v1"
-new_classes = ["RateLimiter"]
+new_sqlite_classes = ["RateLimiter"]
```
diff --git a/src/content/docs/durable-objects/examples/durable-object-in-memory-state.mdx b/src/content/docs/durable-objects/examples/durable-object-in-memory-state.mdx
index 79f15fb8f4bfd62..6b6e8ad18d931b2 100644
--- a/src/content/docs/durable-objects/examples/durable-object-in-memory-state.mdx
+++ b/src/content/docs/durable-objects/examples/durable-object-in-memory-state.mdx
@@ -67,7 +67,7 @@ New Location: ${request.cf.city}`);
}
```
-Finally, configure your Wrangler file to include a Durable Object [binding](/durable-objects/get-started/tutorial/#5-configure-durable-object-bindings) and [migration](/durable-objects/reference/durable-objects-migrations/) based on the namespace and class name chosen previously.
+Finally, configure your Wrangler file to include a Durable Object [binding](/durable-objects/get-started/#4-configure-durable-object-bindings) and [migration](/durable-objects/reference/durable-objects-migrations/) based on the namespace and class name chosen previously.
@@ -80,7 +80,7 @@ class_name = "Location"
[[migrations]]
tag = "v1"
-new_classes = ["Location"]
+new_sqlite_classes = ["Location"]
```
diff --git a/src/content/docs/durable-objects/examples/durable-object-ttl.mdx b/src/content/docs/durable-objects/examples/durable-object-ttl.mdx
index 14e02bf77056d76..2bd0a6d5448ff99 100644
--- a/src/content/docs/durable-objects/examples/durable-object-ttl.mdx
+++ b/src/content/docs/durable-objects/examples/durable-object-ttl.mdx
@@ -96,7 +96,7 @@ export default {
-To test and deploy this example, configure your Wrangler file to include a Durable Object [binding](/durable-objects/get-started/tutorial/#5-configure-durable-object-bindings) and [migration](/durable-objects/reference/durable-objects-migrations/) based on the namespace and class name chosen previously.
+To test and deploy this example, configure your Wrangler file to include a Durable Object [binding](/durable-objects/get-started/#4-configure-durable-object-bindings) and [migration](/durable-objects/reference/durable-objects-migrations/) based on the namespace and class name chosen previously.
@@ -109,7 +109,7 @@ class_name = "MyDurableObject"
[[migrations]]
tag = "v1"
-new_classes = ["MyDurableObject"]
+new_sqlite_classes = ["MyDurableObject"]
```
diff --git a/src/content/docs/durable-objects/examples/websocket-hibernation-server.mdx b/src/content/docs/durable-objects/examples/websocket-hibernation-server.mdx
index 088863127125019..900b324f7ee351d 100644
--- a/src/content/docs/durable-objects/examples/websocket-hibernation-server.mdx
+++ b/src/content/docs/durable-objects/examples/websocket-hibernation-server.mdx
@@ -188,7 +188,7 @@ export class WebSocketHibernationServer extends DurableObject {
-Finally, configure your Wrangler file to include a Durable Object [binding](/durable-objects/get-started/tutorial/#5-configure-durable-object-bindings) and [migration](/durable-objects/reference/durable-objects-migrations/) based on the namespace and class name chosen previously.
+Finally, configure your Wrangler file to include a Durable Object [binding](/durable-objects/get-started/#4-configure-durable-object-bindings) and [migration](/durable-objects/reference/durable-objects-migrations/) based on the namespace and class name chosen previously.
@@ -201,7 +201,7 @@ class_name = "WebSocketHibernationServer"
[[migrations]]
tag = "v1"
-new_classes = ["WebSocketHibernationServer"]
+new_sqlite_classes = ["WebSocketHibernationServer"]
```
diff --git a/src/content/docs/durable-objects/examples/websocket-server.mdx b/src/content/docs/durable-objects/examples/websocket-server.mdx
index 71766b05988c3d2..c4bae3392b8faa9 100644
--- a/src/content/docs/durable-objects/examples/websocket-server.mdx
+++ b/src/content/docs/durable-objects/examples/websocket-server.mdx
@@ -192,7 +192,7 @@ export class WebSocketServer extends DurableObject {
-Finally, configure your Wrangler file to include a Durable Object [binding](/durable-objects/get-started/tutorial/#5-configure-durable-object-bindings) and [migration](/durable-objects/reference/durable-objects-migrations/) based on the namespace and class name chosen previously.
+Finally, configure your Wrangler file to include a Durable Object [binding](/durable-objects/get-started/#4-configure-durable-object-bindings) and [migration](/durable-objects/reference/durable-objects-migrations/) based on the namespace and class name chosen previously.
@@ -205,7 +205,7 @@ class_name = "WebSocketServer"
[[migrations]]
tag = "v1"
-new_classes = ["WebSocketServer"]
+new_sqlite_classes = ["WebSocketServer"]
```
diff --git a/src/content/docs/durable-objects/get-started/tutorial-with-sql-api.mdx b/src/content/docs/durable-objects/get-started.mdx
similarity index 87%
rename from src/content/docs/durable-objects/get-started/tutorial-with-sql-api.mdx
rename to src/content/docs/durable-objects/get-started.mdx
index 206d2fc09c3e38c..8365fb5e2debee8 100644
--- a/src/content/docs/durable-objects/get-started/tutorial-with-sql-api.mdx
+++ b/src/content/docs/durable-objects/get-started.mdx
@@ -1,5 +1,5 @@
---
-title: Tutorial with SQL API
+title: Get started
pcx_content_type: get-started
sidebar:
order: 2
@@ -16,25 +16,21 @@ This guide will instruct you through:
If you wish to learn more about Durable Objects, refer to [What are Durable Objects?](/durable-objects/what-are-durable-objects/).
-:::note[SQLite in Durable Objects Beta]
+## Quick start
-The new beta version of Durable Objects is available where each Durable Object has a private, embedded SQLite database. When deploying a new Durable Object class, users can [opt-in to a SQLite storage backend](/durable-objects/best-practices/access-durable-objects-storage/#sqlite-storage-backend) in order to access new [SQL API](/durable-objects/api/sql-storage/#exec), part of Durable Objects Storage API.
+If you want to skip the steps and get started quickly, click on the button below.
-:::
-
-## Prerequisites
+[](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/templates/tree/staging/hello-world-do-template)
-
+This creates a repository in your GitHub account and deploys the application to Cloudflare Workers. Use this option if you are familiar with Cloudflare Workers, and wish to skip the step-by-step guidance.
-## 1. Enable Durable Objects in the dashboard
+You may wish to manually follow the steps if you are new to Cloudflare Workers.
-To enable Durable Objects, you will need to purchase the Workers Paid plan:
+## Prerequisites
-1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/), and select your account.
-2. Go to **Workers & Pages** > **Plans**.
-3. Select **Purchase Workers Paid** and complete the payment process to enable Durable Objects.
+
-## 2. Create a Worker project to access Durable Objects
+## 1. Create a Worker project
You will access your Durable Object from a [Worker](/workers/). Your Worker application is an interface to interact with your Durable Object.
@@ -66,12 +62,11 @@ Move into your new directory:
cd durable-object-starter
```
-## 3. Write a class to define a Durable Object that uses SQL API
+## 2. Write a Durable Object class using SQL API
Before you create and access a Durable Object, its behavior must be defined by an ordinary exported JavaScript class.
:::note
-
If you do not use JavaScript or TypeScript, you will need a [shim](https://developer.mozilla.org/en-US/docs/Glossary/Shim) to translate your class definition to a JavaScript class.
:::
@@ -140,11 +135,11 @@ export class MyDurableObject extends DurableObject {
In the code above, you have:
1. Defined a RPC method, `sayHello()`, that can be called by a Worker to communicate with a Durable Object.
-2. Accessed a Durable Object's attached storage, which is a private SQLite database only accessible to the object, using [SQL API](/durable-objects/api/sql-storage/#exec) methods (`sql.exec()`) available on `ctx.storage` .
+2. Accessed a Durable Object's attached storage, which is a private SQLite database only accessible to the object, using [SQL API](/durable-objects/api/storage-api/#exec) methods (`sql.exec()`) available on `ctx.storage` .
3. Returned an object representing the single row query result using `one()`, which checks that the query result has exactly one row.
4. Return the `greeting` column from the row object result.
-## 4. Instantiate and communicate with a Durable Object
+## 3. Instantiate and communicate with a Durable Object
:::note
@@ -201,7 +196,7 @@ In the code above, you have:
Refer to [Access a Durable Object from a Worker](/durable-objects/best-practices/create-durable-object-stubs-and-send-requests/) to learn more about communicating with a Durable Object.
-## 5. Configure Durable Object bindings
+## 4. Configure Durable Object bindings
[Bindings](/workers/runtime-apis/bindings/) allow your Workers to interact with resources on the Cloudflare developer platform. The Durable Object bindings in your Worker project's [Wrangler configuration file](/workers/wrangler/configuration/) will include a binding name (for this guide, use `MY_DURABLE_OBJECT`) and the class name (`MyDurableObject`).
@@ -215,13 +210,13 @@ class_name = "MyDurableObject"
-The `[[durable_objects.bindings]]` section contains the following fields:
+The `bindings` section contains the following fields:
- `name` - Required. The binding name to use within your Worker.
- `class_name` - Required. The class name you wish to bind to.
- `script_name` - Optional. Defaults to the current [environment's](/durable-objects/reference/environments/) Worker code.
-## 6. Configure Durable Object class with SQLite storage backend
+## 5. Configure Durable Object class with SQLite storage backend
A migration is a mapping process from a class name to a runtime state. You perform a migration when creating a new Durable Object class, or when renaming, deleting or transferring an existing Durable Object class.
@@ -241,7 +236,7 @@ new_sqlite_classes = ["MyDurableObject"] # Array of new classes
Refer to [Durable Objects migrations](/durable-objects/reference/durable-objects-migrations/) to learn more about the migration process.
-## 7. Develop a Durable Object Worker locally
+## 6. Develop a Durable Object Worker locally
To test your Durable Object locally, run [`wrangler dev`](/workers/wrangler/commands/#dev):
@@ -251,7 +246,7 @@ npx wrangler dev
In your console, you should see a`Hello world` string returned by the Durable Object.
-## 8. Deploy your Durable Object Worker
+## 7. Deploy your Durable Object Worker
To deploy your Durable Object Worker:
@@ -265,7 +260,7 @@ Preview your Durable Object Worker at `..workers.de
By finishing this tutorial, you have successfully created, tested and deployed a Durable Object.
-### Related resources
+## Related resources
- [Create Durable Object stubs](/durable-objects/best-practices/create-durable-object-stubs-and-send-requests/)
- [Access Durable Objects Storage](/durable-objects/best-practices/access-durable-objects-storage/)
diff --git a/src/content/docs/durable-objects/get-started/index.mdx b/src/content/docs/durable-objects/get-started/index.mdx
deleted file mode 100644
index 2fb8ad571f459cd..000000000000000
--- a/src/content/docs/durable-objects/get-started/index.mdx
+++ /dev/null
@@ -1,12 +0,0 @@
----
-pcx_content_type: navigation
-title: Get started
-sidebar:
- order: 3
- group:
- hideIndex: true
----
-
-import { DirectoryListing } from "~/components";
-
-
diff --git a/src/content/docs/durable-objects/get-started/tutorial.mdx b/src/content/docs/durable-objects/get-started/tutorial.mdx
deleted file mode 100644
index 6b24023470cd826..000000000000000
--- a/src/content/docs/durable-objects/get-started/tutorial.mdx
+++ /dev/null
@@ -1,269 +0,0 @@
----
-title: Tutorial
-pcx_content_type: get-started
-sidebar:
- order: 1
----
-
-import { Render, TabItem, Tabs, PackageManagers, WranglerConfig } from "~/components";
-
-This guide will instruct you through:
-
-- Writing a Durable Object class.
-- Writing a Worker which invokes methods on a Durable Object.
-- Deploying a Durable Object.
-
-If you wish to learn more about Durable Objects, refer to [What are Durable Objects?](/durable-objects/what-are-durable-objects/).
-
-## Prerequisites
-
-
-
-## 1. Enable Durable Objects in the dashboard
-
-To enable Durable Objects, you will need to purchase the Workers Paid plan:
-
-1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/), and select your account.
-2. Go to **Workers & Pages** > **Plans**.
-3. Select **Purchase Workers Paid** and complete the payment process to enable Durable Objects.
-
-## 2. Create a Worker project
-
-Durable Objects are accessed from a [Worker](/workers/).
-
-To create a Worker project, run:
-
-
-
-Running `create cloudflare@latest` will install [Wrangler](/workers/wrangler/install-and-update/), the Workers CLI. You will use Wrangler to test and deploy your project.
-
-
-
-This will create a new directory, which will include either a `src/index.js` or `src/index.ts` file to write your code and a [`wrangler.jsonc`](/workers/wrangler/configuration/) configuration file.
-
-Move into your new directory:
-
-```sh
-cd durable-object-starter
-```
-
-## 3. Write a Durable Object class
-
-Durable Objects are defined by a exporting a standard JavaScript class which extends from the `DurableObject` base class.
-
-:::note
-
-If you do not use JavaScript or TypeScript, you will need a [shim](https://developer.mozilla.org/en-US/docs/Glossary/Shim) to translate your class definition to a JavaScript class.
-:::
-
-Your `MyDurableObject` class will have a constructor with two parameters. The first parameter, `state`, passed to the class constructor contains state specific to the Durable Object, including methods for accessing storage. The second parameter, `env`, contains any bindings you have associated with the Worker when you uploaded it.
-
-
-
-```js
-import { DurableObject } from "cloudflare:workers";
-
-export class MyDurableObject extends DurableObject {
- constructor(state, env) {}
-}
-```
-
-
-
-```ts
-import { DurableObject } from "cloudflare:workers";
-
-export class MyDurableObject extends DurableObject {
- constructor(state: DurableObjectState, env: Env) {}
-}
-```
-
-
-
-Workers can invoke public methods defined on a Durable Object via Remote Procedure Call (RPC).
-
-The `sayHello` method demonstrates this capability:
-
-
-
-```js
-import { DurableObject } from "cloudflare:workers";
-
-export class MyDurableObject extends DurableObject {
- constructor(state, env) {}
-
- async sayHello() {
- return "Hello, World!";
- }
-}
-```
-
-
-
-```ts
-import { DurableObject } from "cloudflare:workers";
-
-export class MyDurableObject extends DurableObject {
- constructor(state: DurableObjectState, env: Env) {}
-
- async sayHello(): Promise {
- return "Hello, World!";
- }
-}
-```
-
-
-
-## 4. Invoke methods on a Durable Object class
-
-As mentioned previously, methods on a Durable Object class are invoked by a Worker. This is done by creating an ID refering to an instance of the Durable Object class, getting a stub that refers to a particular instance of a Durable Object class, and invoking methods on that stub.
-
-The fetch handler should look like the following:
-
-
-
-```js
-// Worker
-export default {
- async fetch(request, env) {
- // Every unique ID refers to an individual instance of the Durable Object class
- const id = env.MY_DURABLE_OBJECT.idFromName("foo");
-
- // A stub is a client used to invoke methods on the Durable Object
- const stub = env.MY_DURABLE_OBJECT.get(id);
-
- // Methods on the Durable Object are invoked via the stub
- const rpcResponse = await stub.sayHello();
-
- return new Response(rpcResponse);
- },
-};
-```
-
-
-
-```ts
-// Worker
-export default {
- async fetch(request, env, ctx): Promise {
- // Every unique ID refers to an individual instance of the Durable Object class
- const id = env.MY_DURABLE_OBJECT.idFromName("foo");
-
- // A stub is a client used to invoke methods on the Durable Object
- const stub = env.MY_DURABLE_OBJECT.get(id);
-
- // Methods on the Durable Object are invoked via the stub
- const rpcResponse = await stub.sayHello();
-
- return new Response(rpcResponse);
- },
-} satisfies ExportedHandler;
-```
-
-
-
-## 5. Configure Durable Object bindings
-
-To allow a Worker to invoke methods on a Durable Object, the Worker must have a [Durable Object binding](/workers/runtime-apis/bindings/) in the project's [Wrangler configuration file](/workers/wrangler/configuration/#durable-objects). The binding is configured to use a particular Durable Object class.
-
-
-
-```toml
-[[durable_objects.bindings]]
-name = "MY_DURABLE_OBJECT"
-class_name = "MyDurableObject"
-```
-
-
-
-The `[[durable_objects.bindings]]` section contains the following fields:
-
-- `name` - Required. The binding name to use within your Worker.
-- `class_name` - Required. The class name you wish to bind to.
-- `script_name` - Optional. The name of the Worker if the Durable Object is external to this Worker.
-- `environment` - Optional. The environment of the `script_name` to bind to.
-
-Refer to [Wrangler Configuration](/workers/wrangler/configuration/#durable-objects) for more detail.
-
-## 6. Configure Durable Object classes with migrations
-
-A migration is a mapping process from a class name to a runtime state. You perform a migration when creating a new Durable Object class, or when renaming, deleting or transferring an existing Durable Object class.
-
-Migrations are performed through the `[[migrations]]` configurations key in your Wrangler file.
-
-The Durable Object migration to create a new Durable Object class will look like the following in your Worker's Wrangler file:
-
-
-
-```toml
-[[migrations]]
-tag = "v1" # Should be unique for each entry
-new_classes = ["MyDurableObject"] # Array of new classes
-```
-
-
-
-### 6.a Optional: Configure new Durable Object class for SQL storage
-
-:::note[SQLite in Durable Objects Beta]
-
-New beta version of Durable Objects is available where each Durable Object has a private, embedded SQLite database. SQL storage is opt-in during beta; otherwise, a Durable Object class has the standard, private key-value storage. Objects can access long-lived durable storage with the [Storage API](/durable-objects/api/storage-api/).
-
-:::
-
-A Durable Object class can only have a single storage type, which cannot be changed after the Durable Object class is created.
-
-To configure SQL storage and API, replace `new_classes` with `new_sqlite_classes` in your Worker's Wrangler file:
-
-
-
-```toml
-[[migrations]]
-tag = "v1" # Should be unique for each entry
-new_sqlite_classes = ["MyDurableObject"] # Array of new classes
-```
-
-
-
-Refer to [Durable Objects migrations](/durable-objects/reference/durable-objects-migrations/) to learn more about the migration process.
-
-## 7. Develop a Durable Object Worker locally
-
-To test your Durable Object locally, run [`wrangler dev`](/workers/wrangler/commands/#dev):
-
-```sh
-npx wrangler dev
-```
-
-In your console, you should see a`Hello world` string returned by the Durable Object.
-
-## 8. Deploy your Durable Object Worker
-
-To deploy your Durable Object Worker:
-
-```sh
-npx wrangler deploy
-```
-
-Once deployed, you should be able to see your newly created Durable Object Worker on the [Cloudflare dashboard](https://dash.cloudflare.com/), **Workers & Pages** > **Overview**.
-
-Preview your Durable Object Worker at `..workers.dev`.
-
-By finishing this tutorial, you have successfully created, tested and deployed a Durable Object.
-
-### Related resources
-
-- [Send requests to Durable Objects](/durable-objects/best-practices/create-durable-object-stubs-and-send-requests/)
-- [Miniflare](https://github.com/cloudflare/workers-sdk/tree/main/packages/miniflare) - Helpful tools for mocking and testing your Durable Objects.
diff --git a/src/content/docs/durable-objects/index.mdx b/src/content/docs/durable-objects/index.mdx
index 610045a6194cf6b..1cb686b7b5fe107 100644
--- a/src/content/docs/durable-objects/index.mdx
+++ b/src/content/docs/durable-objects/index.mdx
@@ -13,30 +13,28 @@ head:
import { Render, CardGrid, Description, Feature, LinkTitleCard, Plan, RelatedProduct, LinkButton } from "~/components"
-Create collaborative applications, real-time chat, multiplayer games and more without needing to coordinate state or manage infrastructure.
+Create AI agents, collaborative applications, real-time interactions like chat, and more without needing to coordinate state, have separate storage, or manage infrastructure.
-
+
Durable Objects provide a building block for stateful applications and distributed systems.
-Use Durable Objects to build applications that need coordination among multiple clients, like collaborative editing tools, interactive chat, multiplayer games, and deep distributed systems, without requiring you to build serialization and coordination primitives on your own.
+Use Durable Objects to build applications that need coordination among multiple clients, like collaborative editing tools, interactive chat, multiplayer games, live notifications, and deep distributed systems, without requiring you to build serialization and coordination primitives on your own.
-### What are Durable Objects?
-
-
-
-For more information, refer to the full [What are Durable Objects?](/durable-objects/what-are-durable-objects/) page.
+Get started
-Get started
+:::note
+SQLite-backed Durable Objects are now available on the Workers Free plan with these [limits](/durable-objects/platform/pricing/).
-:::note[SQLite in Durable Objects Beta]
+[SQLite storage](/durable-objects/best-practices/access-durable-objects-storage/) and corresponding [Storage API](/durable-objects/api/storage-api/) methods like `sql.exec` have moved from beta to general availability. New Durable Object classes should use wrangler configuration for [SQLite storage](/durable-objects/best-practices/access-durable-objects-storage/#wrangler-configuration-for-sqlite-durable-objects).
+:::
-The new beta version of Durable Objects is available where each Durable Object has a private, embedded SQLite database. When deploying a new Durable Object class, users can [opt-in to a SQLite storage backend](/durable-objects/best-practices/access-durable-objects-storage/#sqlite-storage-backend) in order to access new [SQL API](/durable-objects/api/sql-storage/#exec) and [point-in-time-recovery API](/durable-objects/api/sql-storage/#point-in-time-recovery), part of Durable Objects Storage API.
+### What are Durable Objects?
-Storage API billing is not enabled for Durable Object classes using SQLite storage backend. SQLite-backed Durable Objects will incur [charges for requests and duration](/durable-objects/platform/pricing/#billing-metrics). We plan to enable Storage API billing for Durable Objects using SQLite storage backend in the first half of 2025 after advance notice with the following [pricing](/durable-objects/platform/pricing/#sqlite-storage-backend).
+
-:::
+For more information, refer to the full [What are Durable Objects?](/durable-objects/what-are-durable-objects/) page.
***
@@ -83,7 +81,7 @@ Cloudflare Workers provides a serverless execution environment that allows you t
-D1 is Cloudflare’s SQL-based native serverless database. Create a database by importing data or defining your tables and writing your queries within a Worker or through the API.
+D1 is Cloudflare's SQL-based native serverless database. Create a database by importing data or defining your tables and writing your queries within a Worker or through the API.
diff --git a/src/content/docs/durable-objects/platform/limits.mdx b/src/content/docs/durable-objects/platform/limits.mdx
index 58f718eb16ff8a3..0c341a79e146ec8 100644
--- a/src/content/docs/durable-objects/platform/limits.mdx
+++ b/src/content/docs/durable-objects/platform/limits.mdx
@@ -6,41 +6,71 @@ sidebar:
---
-import { Render, GlossaryTooltip, WranglerConfig } from "~/components";
+import { Render, GlossaryTooltip, Details, WranglerConfig } from "~/components";
-Durable Objects are only available on the [Workers Paid plan](/workers/platform/pricing/#workers). Durable Objects limits are the same as [Workers Limits](/workers/platform/limits/), as well as the following limits that are specific to Durable Objects:
+Durable Objects are a special kind of Worker, so [Workers Limits](/workers/platform/limits/) apply according to your Workers plan. In addition, Durable Objects have specific limits as listed in this page.
-| Feature | Limit for class with key-value storage backend | Limit for class with SQLite storage backend [^1] |
-| --------------------------------- | ---------------------------------------------------------------- | ----------------------------------------------- |
-| Number of Objects | Unlimited (within an account or of a given class) | Unlimited (within an account or of a given class) |
-| Maximum Durable Object namespaces | 500 (identical to the [script limit](/workers/platform/limits/)) | 500 (identical to the [script limit](/workers/platform/limits/)) |
-| Storage per account | 50 GB (can be raised by contacting Cloudflare) [^2] | 50 GB (can be raised by contacting Cloudflare) [^2] |
-| Storage per class | Unlimited | Unlimited |
-| Storage per Durable Object | Unlimited | 1 GB [^3] |
-| Key size | 2 KiB (2048 bytes) | Key and value combined cannot exceed 2 MB |
-| Value size | 128 KiB (131072 bytes) | Key and value combined cannot exceed 2 MB |
-| WebSocket message size | 1 MiB (only for received messages) | 1 MiB (only for received messages) |
-| CPU per request | 30s (including WebSocket messages) [^4] | 30 seconds (default) / configurable to 5 minutes of [active CPU time](/workers/platform/limits/#cpu-time) [^4] |
+## SQLite-backed Durable Objects general limits
-[^1]: The new beta version of Durable Objects is available where each Durable Object has a private, embedded SQLite database. When creating a Durable Object class, users can [opt-in to using SQL storage](/durable-objects/reference/durable-objects-migrations/#enable-sqlite-storage-backend-on-new-durable-object-class-migration).
+| Feature | Limit |
+| ---------------------------------------- | ----------------------------------------------------------- |
+| Number of Objects | Unlimited (within an account or of a given class) |
+| Maximum Durable Object classes | 500 (Workers Paid) / 100 (Free) [^1] |
+| Storage per account | Unlimited (Workers Paid) / 5GB (Free) [^2] |
+| Storage per class | Unlimited [^3] |
+| Storage per Durable Object | 10 GB [^3] |
+| Key size | Key and value combined cannot exceed 2 MB |
+| Value size | Key and value combined cannot exceed 2 MB |
+| WebSocket message size | 1 MiB (only for received messages) |
+| CPU per request | 30 seconds (default) / configurable to 5 minutes of [active CPU time](/workers/platform/limits/#cpu-time) [^4] |
-[^2]: Durable Objects both bills and measures storage based on a gigabyte
(1 GB = 1,000,000,000 bytes) and not a gibibyte (GiB).
+
+1: Identical to the Workers [script limit](/workers/platform/limits/)
-[^3]: Will be raised to 10 GB for general availability.
+2: Durable Objects both bills and measures storage based on a gigabyte
(1 GB = 1,000,000,000 bytes) and not a gibibyte (GiB).
-[^4]: Each incoming HTTP request or WebSocket *message* resets the remaining available CPU time to 30 seconds. This allows the Durable Object to consume up to 30 seconds of compute after each incoming network request, with each new network request resetting the timer. If you consume more than 30 seconds of compute between incoming network requests, there is a heightened chance that the individual Durable Object is evicted and reset.
+3: Accounts on the Workers Free plan are limited to 5GB total Durable Objects storage.
-For Durable Object classes with [SQLite storage backend](/durable-objects/best-practices/access-durable-objects-storage/#sqlite-storage-backend) these SQL limits apply:
+4: Each incoming HTTP request or WebSocket *message* resets the remaining available CPU time to 30 seconds. This allows the Durable Object to consume up to 30 seconds of compute after each incoming network request, with each new network request resetting the timer. If you consume more than 30 seconds of compute between incoming network requests, there is a heightened chance that the individual Durable Object is evicted and reset. CPU time per request invocation [can be increased](/durable-objects/platform/limits/#increasing-durable-object-cpu-limits).
+
-| SQL | Limit |
-| -------------------------------------------------------- | ----- |
-| Maximum number of columns per table | 100 |
+### SQL storage limits
+
+For Durable Object classes with [SQLite storage](/durable-objects/api/storage-api/#sql-storage) these SQL limits apply:
+
+| SQL | Limit |
+| -------------------------------------------------------- | ----------------------------------------------- |
+| Maximum number of columns per table | 100 |
| Maximum number of rows per table | Unlimited (excluding per-object storage limits) |
-| Maximum string, `BLOB` or table row size | 2 MB |
-| Maximum SQL statement length | 100 KB |
-| Maximum bound parameters per query | 100 |
-| Maximum arguments per SQL function | 32 |
-| Maximum characters (bytes) in a `LIKE` or `GLOB` pattern | 50 bytes |
+| Maximum string, `BLOB` or table row size | 2 MB |
+| Maximum SQL statement length | 100 KB |
+| Maximum bound parameters per query | 100 |
+| Maximum arguments per SQL function | 32 |
+| Maximum characters (bytes) in a `LIKE` or `GLOB` pattern | 50 bytes |
+
+## Key-value backed Durable Objects general limits
+
+
+
+| Feature | Limit for class with key-value storage backend |
+| ---------------------------------------- | ---------------------------------------------------------------- |
+| Number of Objects | Unlimited (within an account or of a given class) |
+| Maximum Durable Object classes | 500 (Workers Paid) / 100 (Free) [^1] |
+| Storage per account | 50 GB (can be raised by contacting Cloudflare) [^2] |
+| Storage per class | Unlimited |
+| Storage per Durable Object | Unlimited |
+| Key size | 2 KiB (2048 bytes) |
+| Value size | 128 KiB (131072 bytes) |
+| WebSocket message size | 1 MiB (only for received messages) |
+| CPU per request | 30s (including WebSocket messages) [^3] |
+
+
+1: Identical to the Workers [script limit](/workers/platform/limits/)
+
+2: Durable Objects both bills and measures storage based on a gigabyte
(1 GB = 1,000,000,000 bytes) and not a gibibyte (GiB).
+
+3: Each incoming HTTP request or WebSocket *message* resets the remaining available CPU time to 30 seconds. This allows the Durable Object to consume up to 30 seconds of compute after each incoming network request, with each new network request resetting the timer. If you consume more than 30 seconds of compute between incoming network requests, there is a heightened chance that the individual Durable Object is evicted and reset. CPU time per request invocation [can be increased](/durable-objects/platform/limits/#increasing-durable-object-cpu-limits).
+
diff --git a/src/content/docs/durable-objects/platform/pricing.mdx b/src/content/docs/durable-objects/platform/pricing.mdx
index c298448da7e4c19..97345fc2af0f8ed 100644
--- a/src/content/docs/durable-objects/platform/pricing.mdx
+++ b/src/content/docs/durable-objects/platform/pricing.mdx
@@ -3,16 +3,21 @@ pcx_content_type: concept
title: Pricing
sidebar:
order: 1
-
---
import { Render } from "~/components"
-## Billing metrics
+Durable Objects can incur two types of billing: compute and storage.
+
+
+
+On Workers Free plan:
+- If you exceed any one of the free tier limits, further operations of that type will fail with an error.
+- Daily free limits reset at 00:00 UTC.
-
+
-## Durable Objects billing examples
+## Compute billing examples
These examples exclude the costs for the Workers calling the Durable Objects. When modelling the costs of a Durable Object, note that:
@@ -115,10 +120,6 @@ In this scenario, the estimated monthly cost would be calculated as:
2 The example uses 1 second because each Durable Object is active for 1 second per minute. This can also be thought of as 432 million requests that each take 10 ms to execute (4,320,000 seconds).
-## Storage API billing
-
-
-
## Frequently Asked Questions
### Does an empty table / SQLite database contribute to my storage?
diff --git a/src/content/docs/durable-objects/reference/durable-objects-migrations.mdx b/src/content/docs/durable-objects/reference/durable-objects-migrations.mdx
index 39e236a4d948706..4523f511c1eeaff 100644
--- a/src/content/docs/durable-objects/reference/durable-objects-migrations.mdx
+++ b/src/content/docs/durable-objects/reference/durable-objects-migrations.mdx
@@ -5,7 +5,7 @@ sidebar:
order: 2
---
-import { GlossaryTooltip, WranglerConfig, Steps, Details } from "~/components";
+import { Render, GlossaryTooltip, WranglerConfig, Steps, Details } from "~/components";
A migration is a mapping process from a class name to a runtime state. This process communicates the changes to the Workers runtime and provides the runtime with instructions on how to deal with those changes.
@@ -40,14 +40,14 @@ To apply a Create migration:
```toml
[[migrations]]
tag = "" # Migration identifier. This should be unique for each migration entry
- new_classes = [""] # Array of new classes
+ new_sqlite_classes = [""] # Array of new classes
# For SQLite storage backend use new_sqlite_classes=[""] instead
```
The Create migration contains:
- A `tag` to identify the migration.
- - The array `new_classes`, which contains the new Durable Object class.
+ - The array `new_sqlite_classes`, which contains the new Durable Object class.
2. Ensure you reference the correct name of the Durable Object class in your Worker code.
3. Deploy the Worker.
@@ -67,12 +67,34 @@ class_name = "DurableObjectAClass"
# Add the lines below for a Create migration.
[[migrations]]
tag = "v1"
-new_classes = ["DurableObjectAClass"]
+new_sqlite_classes = ["DurableObjectAClass"]
```
+### Create Durable Object class with key-value storage
+
+:::note[Recommended SQLite-backed Durable Objects]
+Cloudflare recommends all new Durable Object classes use the [SQLite storage backend](/durable-objects/best-practices/access-durable-objects-storage/#create-sqlite-backed-durable-object-class).
+
+The key-value storage backend remains for backwards compatibility.
+:::
+
+Use `new_classes` on the migration in your Worker's Wrangler file to create a Durable Object class with the key-value storage backend:
+
+
+
+```toml
+[[migrations]]
+tag = "v1" # Should be unique for each entry
+new_classes = ["MyDurableObject"] # Array of new classes
+```
+
+
+
+
+
## Delete migration
Running a Delete migration will delete all Durable Objects associated with the deleted class, including all of their stored data.
@@ -114,7 +136,7 @@ To delete a Durable Object binding `DEPRECATED_OBJECT`, your `wrangler.toml / wr
[[migrations]]
tag = "v3" # Should be unique for each entry
-deleted_classes = ["DeprecatedObjectClass"] # Array of new classes
+deleted_classes = ["DeprecatedObjectClass"] # Array of deleted classes
```
@@ -263,33 +285,12 @@ To illustrate an example migrations workflow, the `DurableObjectExample` class c
# Creating a new Durable Object class
[[migrations]]
tag = "v1" # Migration identifier. This should be unique for each migration entry
-new_classes = ["DurableObjectExample"] # Array of new classes
+new_sqlite_classes = ["DurableObjectExample"] # Array of new classes
```
You can rename the `DurableObjectExample` class to `UpdatedName` and delete an outdated `DeprecatedClass` entirely. You can create separate migrations for each operation, or combine them into a single migration as shown below. */}
-## Enable SQLite storage backend on new Durable Object class migration
-
-:::note[SQLite in Durable Objects Beta]
-
-The new beta version of Durable Objects is available where each Durable Object has a private, embedded SQLite database. When deploying a new Durable Object class, users can opt-in to a SQLite storage backend in order to access new [SQL API](/durable-objects/api/sql-storage/#exec). Otherwise, a Durable Object class has a key-value storage backend.
-
-:::
-
-To allow a new Durable Object class to use a SQLite storage backend, use `new_sqlite_classes` on the migration in your Worker's `wrangler` configuration file:
-
-
-
-```toml
-[[migrations]]
-tag = "v1" # Should be unique for each entry
-new_sqlite_classes = ["MyDurableObject"] # Array of new classes
-```
-
-
-
-For an example of a new class migration, refer to [Get started: Configure Durable Object class with SQLite storage backend](/durable-objects/get-started/tutorial-with-sql-api/#6-configure-durable-object-class-with-sqlite-storage-backend).
You cannot enable a SQLite storage backend on an existing, deployed Durable Object class, so setting `new_sqlite_classes` on later migrations will fail with an error. Automatic migration of deployed classes from their key-value storage backend to SQLite storage backend will be available in the future.
diff --git a/src/content/docs/durable-objects/reference/environments.mdx b/src/content/docs/durable-objects/reference/environments.mdx
index 95e05c25c346a37..a57eedae40d6384 100644
--- a/src/content/docs/durable-objects/reference/environments.mdx
+++ b/src/content/docs/durable-objects/reference/environments.mdx
@@ -8,6 +8,10 @@ sidebar:
import { WranglerConfig } from "~/components";
+Environments provide isolated spaces where your code runs with specific dependencies and configurations. This can be useful for a number of reasons, such as compatibility testing or version management. Using different environments can help with code consistency, testing, and production segregation, which reduces the risk of errors when deploying code.
+
+## Wrangler environments
+
[Wrangler](/workers/wrangler/install-and-update/) allows you to deploy the same Worker application with different configuration for each [environment](/workers/wrangler/environments/).
If you are using Wrangler environments, you must specify any [Durable Object bindings](/workers/runtime-apis/bindings/) you wish to use on a per-environment basis.
@@ -56,3 +60,25 @@ durable_objects.bindings = [
```
+
+## Local development
+
+Local development sessions create a standalone, local-only environment that mirrors the production environment, so that you can test your Worker and Durable Objects before you deploy to production.
+
+An existing Durable Object binding of `DB` would be available to your Worker when running locally.
+
+Refer to Workers [Local development](/workers/local-development/#supported-resource-bindings-in-different-environments).
+
+## Remote development
+
+KV-backed Durable Objects support remote development using the dashboard playground. The dashboard playground uses a browser version of Visual Studio Code, allowing you to rapidly iterate on your Worker entirely in your browser.
+
+To start remote development:
+
+1. Log in to your Cloudflare dashboard, and go to [**Workers & Pages** > **Overview**](https://dash.cloudflare.com/?to=/:account/workers-and-pages).
+2. Select an existing Worker.
+3. Select the **Edit code** icon located on the upper-right of the screen.
+
+:::caution
+Remote development is only available for KV-backed Durable Objects. SQLite-backed Durable Objects do not support remote development.
+:::
\ No newline at end of file
diff --git a/src/content/docs/durable-objects/what-are-durable-objects.mdx b/src/content/docs/durable-objects/what-are-durable-objects.mdx
index 72d7e57de6f1339..70ada5dc26ff7f8 100644
--- a/src/content/docs/durable-objects/what-are-durable-objects.mdx
+++ b/src/content/docs/durable-objects/what-are-durable-objects.mdx
@@ -49,7 +49,7 @@ In-memory state is reset when the Durable Object hibernates after being idle for
The [Durable Object Storage API](/durable-objects/api/storage-api/) allows Durable Objects to access fast, transactional, and strongly consistent storage. A Durable Object's attached storage is private to its unique instance and cannot be accessed by other objects.
-There are two flavors of the storage API, a [key-value (KV) API](/durable-objects/api/storage-api/#methods) and an [SQL API](/durable-objects/api/sql-storage/).
+There are two flavors of the storage API, a [key-value (KV) API](/durable-objects/api/storage-api/#kv-api) and an [SQL API](/durable-objects/api/storage-api/#sql-api).
When using the [new SQLite in Durable Objects storage backend](/durable-objects/reference/durable-objects-migrations/#enable-sqlite-storage-backend-on-new-durable-object-class-migration), you have access to both the APIs. However, if you use the previous storage backend you only have access to the key-value API.
@@ -108,6 +108,6 @@ Finally, the following blog posts may help you learn some of the technical imple
## Get started
-Get started now by following the ["Tutorial with SQL API"](/durable-objects/get-started/tutorial-with-sql-api/) to create your first application using Durable Objects.
+Get started now by following the ["Get started" guide](/durable-objects/get-started/) to create your first application using Durable Objects.
[^1]: Storage per Durable Object with SQLite is currently 1 GB. This will be raised to 10 GB for general availability.
\ No newline at end of file
diff --git a/src/content/docs/kv/concepts/kv-bindings.mdx b/src/content/docs/kv/concepts/kv-bindings.mdx
index 7a1f5d4c4f8298b..de4edacf412c022 100644
--- a/src/content/docs/kv/concepts/kv-bindings.mdx
+++ b/src/content/docs/kv/concepts/kv-bindings.mdx
@@ -78,7 +78,7 @@ kv_namespaces = [
## Access KV from Durable Objects and Workers using ES modules format
-[Durable Objects](/durable-objects/) use ES modules format. Instead of a global variable, bindings are available as properties of the `env` parameter [passed to the constructor](/durable-objects/get-started/tutorial/#3-write-a-durable-object-class).
+[Durable Objects](/durable-objects/) use ES modules format. Instead of a global variable, bindings are available as properties of the `env` parameter [passed to the constructor](/durable-objects/get-started/#2-write-a-durable-object-class).
An example might look like:
diff --git a/src/content/docs/queues/examples/use-queues-with-durable-objects.mdx b/src/content/docs/queues/examples/use-queues-with-durable-objects.mdx
index 8bd943eb1f62293..3a22101b637ed69 100644
--- a/src/content/docs/queues/examples/use-queues-with-durable-objects.mdx
+++ b/src/content/docs/queues/examples/use-queues-with-durable-objects.mdx
@@ -38,7 +38,7 @@ bindings = [
[[migrations]]
tag = "v1"
-new_classes = ["YourDurableObject"]
+new_sqlite_classes = ["YourDurableObject"]
```
diff --git a/src/content/docs/workers/local-development.mdx b/src/content/docs/workers/local-development.mdx
index 1d1c5a567280021..df841e2b6f7b294 100644
--- a/src/content/docs/workers/local-development.mdx
+++ b/src/content/docs/workers/local-development.mdx
@@ -40,9 +40,9 @@ npx wrangler dev
| Analytics Engine | ✅ | ✅ |
| Browser Rendering | ❌ | ✅ |
| D1 | ✅ | ✅ |
-| Durable Objects | ✅ | ✅ |
+| Durable Objects | ✅ | ✅ [^2] |
| Email Bindings | ❌ | ✅ |
-| Hyperdrive | ✅[^2] | ✅ |
+| Hyperdrive | ✅[^3] | ✅ |
| Images | ✅ | ✅ |
| KV | ✅ | ✅ |
| mTLS | ❌ | ✅ |
@@ -50,16 +50,18 @@ npx wrangler dev
| R2 | ✅ | ✅ |
| Rate Limiting | ✅ | ✅ |
| Service Bindings (multiple workers) | ✅ | ✅ |
-| Vectorize | ✅[^3] | ✅ |
+| Vectorize | ✅[^4] | ✅ |
| Workflows | ✅ | ❌ |
With any bindings that are not supported locally, you will need to use the [`--remote` command](#develop-using-remote-resources-and-bindings) in wrangler, such as `wrangler dev --remote`.
[^1]: Using Workers AI always accesses your Cloudflare account in order to run AI models and will incur usage charges even in local development.
-[^2]: Using Hyperdrive with local development allows you to connect to a local database (running on `localhost`) but you cannot connect to a remote database. To connect to a remote database, use remote development.
+[^2]: For KV-backed Durable Objects only. SQLite-backed Durable Objects do not support remote development.
-[^3]: Using Vectorize always accesses your Cloudflare account to run queries, and will incur usage charges even in local development.
+[^3]: Using Hyperdrive with local development allows you to connect to a local database (running on `localhost`) but you cannot connect to a remote database. To connect to a remote database, use remote development.
+
+[^4]: Using Vectorize always accesses your Cloudflare account to run queries, and will incur usage charges even in local development.
## Work with local data
diff --git a/src/content/docs/workers/platform/pricing.mdx b/src/content/docs/workers/platform/pricing.mdx
index 728c3bb1c245d55..78ef9418d75fadd 100644
--- a/src/content/docs/workers/platform/pricing.mdx
+++ b/src/content/docs/workers/platform/pricing.mdx
@@ -9,7 +9,9 @@ description: Workers plans and pricing information.
import { GlossaryTooltip, Render } from "~/components";
-By default, users have access to the Workers Free plan. The Workers Free plan includes limited usage of Workers, Pages Functions and Workers KV. Read more about the [Free plan limits](/workers/platform/limits/#worker-limits).
+By default, users have access to the Workers Free plan.
+
+The Workers Free plan includes limited usage of Workers and other products. Daily free limits for these products reset at 00:00 UTC. When the daily limit is reached, further operations of that type will fail with an error, until the limits reset again. For more information on the Free plan, refer to [Free plan limits](/workers/platform/limits/#worker-limits).
The Workers Paid plan includes Workers, Pages Functions, Workers KV, and Durable Objects usage for a minimum charge of $5 USD per month for an account. The plan includes increased initial usage allotments, with clear charges for usage that exceeds the base plan.
@@ -162,7 +164,7 @@ To learn more about Queues pricing and review billing examples, refer to [Queues
## D1
-D1 is available on both the [Workers Free](#workers) and [Workers Paid](#workers) plans.
+D1 is available on both the Workers Free and Workers Paid plans.
@@ -174,18 +176,16 @@ Refer to [D1 Pricing](/d1/platform/pricing/) to learn more about how D1 is bille
## Durable Objects
-
+
+
+
:::note[Durable Objects billing examples]
-For more information and [examples of Durable Objects billing](/durable-objects/platform/pricing/#durable-objects-billing-examples), refer to [Durable Objects Pricing](/durable-objects/platform/pricing/).
+For more information and [examples of Durable Objects billing](/durable-objects/platform/pricing#compute-billing-examples), refer to [Durable Objects Pricing](/durable-objects/platform/pricing/).
:::
-## Durable Objects Storage API
-
-
-
## Vectorize
Vectorize is currently only available on the Workers paid plan.
diff --git a/src/content/docs/workers/platform/storage-options.mdx b/src/content/docs/workers/platform/storage-options.mdx
index 45aa2b104934a75..c36f98677c9d8e4 100644
--- a/src/content/docs/workers/platform/storage-options.mdx
+++ b/src/content/docs/workers/platform/storage-options.mdx
@@ -207,12 +207,6 @@ To get started with Vectorize:
-:::note[SQLite in Durable Objects Beta]
-
-The new beta version of Durable Objects is available where each Durable Object has a private, embedded SQLite database. When deploying a new Durable Object class, users can opt-in to using SQL storage in order to access [Storage SQL API methods](/durable-objects/api/sql-storage/#exec). Otherwise, a Durable Object class has the standard, private key-value storage.
-
-:::
-
## D1 vs Hyperdrive
diff --git a/src/content/docs/workers/tutorials/deploy-a-realtime-chat-app/index.mdx b/src/content/docs/workers/tutorials/deploy-a-realtime-chat-app/index.mdx
index 4699fd9b5e5b446..2a2255692d90c97 100644
--- a/src/content/docs/workers/tutorials/deploy-a-realtime-chat-app/index.mdx
+++ b/src/content/docs/workers/tutorials/deploy-a-realtime-chat-app/index.mdx
@@ -15,8 +15,6 @@ In this tutorial, you will deploy a serverless, real-time chat application that
This chat application uses a Durable Object to control each chat room. Users connect to the Object using WebSockets. Messages from one user are broadcast to all the other users. The chat history is also stored in durable storage. Real-time messages are relayed directly from one user to others without going through the storage layer.
-To continue with this tutorial, you must purchase the [Workers Paid plan](/workers/platform/pricing/#workers) and enable Durable Objects by logging into the [Cloudflare dashboard](https://dash.cloudflare.com) > **Workers & Pages** > select your Worker > **Durable Objects**.
-
## Clone the chat application repository
@@ -98,7 +96,7 @@ bindings = [
# Indicate that you want the ChatRoom and RateLimiter classes to be callable as Durable Objects.
[[migrations]]
tag = "v1" # Should be unique for each entry
-new_classes = ["ChatRoom", "RateLimiter"]
+new_sqlite_classes = ["ChatRoom", "RateLimiter"]
[[migrations]]
tag = "v2"
diff --git a/src/content/docs/workers/tutorials/live-cursors-with-nextjs-rpc-do/index.mdx b/src/content/docs/workers/tutorials/live-cursors-with-nextjs-rpc-do/index.mdx
index 851e03389d6e49f..b899f644fd22593 100644
--- a/src/content/docs/workers/tutorials/live-cursors-with-nextjs-rpc-do/index.mdx
+++ b/src/content/docs/workers/tutorials/live-cursors-with-nextjs-rpc-do/index.mdx
@@ -144,7 +144,7 @@ that will be made available to the Next.js Worker using a [`WorkerEntrypoint`](/
[[migrations]]
tag = "v1"
- new_classes = ["CursorSessions"]
+ new_sqlite_classes = ["CursorSessions"]
```
2. Initialize the main methods for the Durable Object and define types for WebSocket messages and cursor sessions in your `worker/src/index.ts`
to support type-safe interaction:
diff --git a/src/content/docs/workers/wrangler/configuration.mdx b/src/content/docs/workers/wrangler/configuration.mdx
index 1a43e19ec38e77c..fcab4a401b99b85 100644
--- a/src/content/docs/workers/wrangler/configuration.mdx
+++ b/src/content/docs/workers/wrangler/configuration.mdx
@@ -182,6 +182,9 @@ At a minimum, the `name`, `main` and `compatibility_date` keys are required to d
* `assets`
- Configures static assets that will be served. Refer to [Assets](/workers/static-assets/binding/) for more details.
+* `migrations`
+ - Maps a Durable Object from a class name to a runtime state. This communicates changes to the Durable Object (creation / deletion / rename / transfer) to the Workers runtime and provides the runtime with instructions on how to deal with those changes. Refer to [Durable Objects migrations](/durable-objects/reference/durable-objects-migrations/#durable-object-migrations-in-wranglertoml).
+
## Non-inheritable keys
Non-inheritable keys are configurable at the top-level, but cannot be inherited by environments and must be specified for each environment.
@@ -560,7 +563,7 @@ When making changes to your Durable Object classes, you must perform a migration
- A unique identifier for this migration.
-- `new_classes`
+- `new_sqlite_classes`
- The new Durable Objects being defined.
@@ -579,7 +582,7 @@ Example:
```toml title="wrangler.toml"
[[migrations]]
tag = "v1" # Should be unique for each entry
-new_classes = ["DurableObjectExample"] # Array of new classes
+new_sqlite_classes = ["DurableObjectExample"] # Array of new classes
[[migrations]]
tag = "v2"
diff --git a/src/content/glossary/durable-objects.yaml b/src/content/glossary/durable-objects.yaml
index 75b5cc66c89026b..c273982cdbd068d 100644
--- a/src/content/glossary/durable-objects.yaml
+++ b/src/content/glossary/durable-objects.yaml
@@ -71,3 +71,8 @@ entries:
- term: "output gate"
general_definition: |-
When a storage write operation is in progress, any new outgoing network messages will be held back until the write has completed. We say that these messages are waiting for the "output gate" to open. If the write ultimately fails, the outgoing network messages will be discarded and replaced with errors, while the Durable Object will be shut down and restarted from scratch.
+
+ - term: "bookmark"
+ general_definition: |-
+ A bookmark is a mostly alphanumeric string like `0000007b-0000b26e-00001538-0c3e87bb37b3db5cc52eedb93cd3b96b` which represents a specific state of a SQLite database at a certain point in time. Bookmarks are designed to be lexically comparable: a bookmark representing an earlier point in time compares less than one representing a later point, using regular string comparison.
+
diff --git a/src/content/partials/durable-objects/do-plans-note.mdx b/src/content/partials/durable-objects/do-plans-note.mdx
new file mode 100644
index 000000000000000..1dfe793cc087623
--- /dev/null
+++ b/src/content/partials/durable-objects/do-plans-note.mdx
@@ -0,0 +1,12 @@
+---
+{}
+---
+
+:::note
+Durable Objects are available both on Workers Free and Workers Paid plans.
+
+- **Workers Free plan**: Only Durable Objects with [SQLite storage backend](/durable-objects/best-practices/access-durable-objects-storage/#wrangler-configuration-for-sqlite-backed-durable-objects) are available.
+- **Workers Paid plan**: Durable Objects with either SQLite storage backend or [key-value storage backend](/durable-objects/reference/durable-objects-migrations/#create-durable-object-class-with-key-value-storage) are available.
+
+If you wish to downgrade from a Workers Paid plan to a Workers Free plan, you must first ensure that you have deleted all Durable Object namespaces with the key-value storage backend.
+:::
\ No newline at end of file
diff --git a/src/content/partials/durable-objects/do-sqlite-storage-no-bill-note.mdx b/src/content/partials/durable-objects/do-sqlite-storage-no-bill-note.mdx
new file mode 100644
index 000000000000000..5fb2f97e0fb2547
--- /dev/null
+++ b/src/content/partials/durable-objects/do-sqlite-storage-no-bill-note.mdx
@@ -0,0 +1,7 @@
+---
+{}
+---
+
+:::note[Storage billing on SQLite-backed Durable Objects]
+Storage billing is not yet enabled for Durable Object classes using the SQLite storage backend. SQLite-backed Durable Objects will incur [charges for requests and duration](/durable-objects/platform/pricing/#compute-billing). Storage billing for SQLite-backed Durable Objects will be enabled at a later date with advance notice with the [shared pricing](/durable-objects/platform/pricing/#sqlite-storage-backend).
+:::
\ No newline at end of file
diff --git a/src/content/partials/durable-objects/durable-objects-pricing.mdx b/src/content/partials/durable-objects/durable-objects-pricing.mdx
index 8e90c41e5d37a8e..9f6214d2446e0a9 100644
--- a/src/content/partials/durable-objects/durable-objects-pricing.mdx
+++ b/src/content/partials/durable-objects/durable-objects-pricing.mdx
@@ -1,86 +1,98 @@
---
-{}
-
+params:
+ - product
---
-import { Markdown } from "~/components"
-
-## Durable Objects
+import {Render, Markdown, GlossaryTooltip, Details, AnchorHeading } from "~/components";
-Durable Objects are only available on the [Workers Paid plan](/workers/platform/pricing/#workers).
+{ props.product === "durable-objects" && <>> }
+{ props.product === "workers" && <>> }
+Durable Objects are billed only while the Durable Object is active (includes the request context).
+| | Free plan | Paid plan |
+| -------------------- | ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
+| Requests | 100,000 / day | 1 million, + $0.15/million
Includes HTTP requests, RPC sessions1, WebSocket messages2, and alarm invocations |
+| Duration3 | 13,000 GB-s / day | 400,000 GB-s, + $12.50/million GB-s4,5 |
-| | Paid plan |
-| -------------------- | ------------------------------------------------- |
-| Requests1 | 1 million, + $0.15/million |
-| Duration2 | 400,000 GB-s, + $12.50/million GB-s3,4 |
+
+1 Each [RPC session](/workers/runtime-apis/rpc/lifecycle/) is billed as one request to your Durable Object. Every [RPC method call](/durable-objects/best-practices/create-durable-object-stubs-and-send-requests/) on a [Durable Objects stub](/durable-objects/) is its own RPC session and therefore a single billed request.
+RPC method calls can return objects (stubs) extending [`RpcTarget`](/workers/runtime-apis/rpc/lifecycle/#lifetimes-memory-and-resource-management) and invoke calls on those stubs. Subsequent calls on the returned stub are part of the same RPC session and are not billed as separate requests. For example:
-1 Requests include all incoming HTTP requests, WebSocket messages, and alarm invocations. There is no charge for outgoing WebSocket messages, nor for incoming [WebSocket protocol pings](https://www.rfc-editor.org/rfc/rfc6455#section-5.5.2).
+```js
+let durableObjectStub = OBJECT_NAMESPACE.get(id); // retrieve Durable Object stub
+using foo = await durableObjectStub.bar(); // billed as a request
+await foo.baz(); // treated as part of the same RPC session created by calling bar(), not billed as a request
+await durableObjectStub.cat(); // billed as a request
+```
-2 Application-level auto-response messages handled by [`state.setWebSocketAutoResponse()`](/durable-objects/api/state/#setwebsocketautoresponse) will not incur additional wall-clock time, and will not be charged.
+2 A request is needed to create a WebSocket connection. There is no charge for outgoing WebSocket messages, nor for incoming [WebSocket protocol pings](https://www.rfc-editor.org/rfc/rfc6455#section-5.5.2). For compute requests billing-only, a 20:1 ratio is applied to incoming WebSocket messages to factor in smaller messages for real-time communication. For example, 100 WebSocket incoming messages would be charged as 5 requests for billing purposes. The 20:1 ratio does not affect Durable Object metrics and analytics, which reflect actual usage.
-3 Duration is billed in wall-clock time as long as the Object is active, but is shared across all requests active on an Object at once. Once your Object finishes responding to all requests, it will stop incurring duration charges. Calling `accept()` on a WebSocket in an Object will incur duration charges for the entire time the WebSocket is connected. Prefer using [`state.acceptWebSocket()`](/durable-objects/best-practices/websockets/), which will stop incurring duration charges once all event handlers finish running.
+3 Application level auto-response messages handled by [`state.setWebSocketAutoResponse()`](/durable-objects/best-practices/websockets/) will not incur additional wall-clock time, and so they will not be charged.
-4 Duration billing charges for the 128 MB of memory your Durable Object is allocated, regardless of actual usage. If your account creates many instances of a single Durable Object class, Durable Objects may run in the same isolate on the same physical machine and share the 128 MB of memory. These Durable Objects are still billed as if they are allocated a full 128 MB of memory.
+4 Duration is billed in wall-clock time as long as the Object is active, but is shared across all requests active on an Object at once. Calling `accept()` on a WebSocket in an Object will incur duration charges for the entire time the WebSocket is connected. Note that the request context for a Durable Object extends for 10 seconds after the last client disconnects. The request context of a Durable Object is evaluated every 70 seconds and the context is ended if it has been active for 70 seconds. If you prefer, use the [WebSocket Hibernation API](/durable-objects/best-practices/websockets/#websocket-hibernation-api) to avoid incurring duration charges once all event handlers finish running.
-### Durable Objects billing examples
+5 Duration billing charges for the 128 MB of memory your Durable Object is allocated, regardless of actual usage. If your account creates many instances of a single Durable Object class, Durable Objects may run in the same isolate on the same physical machine and share the 128 MB of memory. These Durable Objects are still billed as if they are allocated a full 128 MB of memory.
-These examples exclude the costs for the Workers calling the Durable Objects.
+
-#### Example 1
+{ props.product === "durable-objects" && <>> }
+{ props.product === "workers" && <>> }
-If a single Durable Object was called by a Worker 1.5 million times, and was active for 1,000,000 seconds in the month, the estimated cost in a month would be:
+The [Durable Objects Storage API](/durable-objects/api/storage-api/) is only accessible from within Durable Objects. Pricing depends on the storage backend of your Durable Objects.
-Total = ~$0.08 USD + Minimum $5/mo usage = $5.08
+- **SQLite-backed Durable Objects (recommended)**: [SQLite storage backend](/durable-objects/best-practices/access-durable-objects-storage/#create-sqlite-backed-durable-object-class) is recommended for all new Durable Object classes. Workers Free plan can only create and access SQLite-backed Durable Objects.
+- **Key-value backed Durable Objects**: [Key-value storage backend](/durable-objects/reference/durable-objects-migrations/#create-durable-object-class-with-key-value-storage) is only available on the Workers Paid plan.
-- (1.5 million requests - included 1 million requests) x $0.15 / 1,000,000 = $0.075
-- 1,000,000 seconds \* 128 MB / 1 GB = 128,000 GB-s
-- (128,000 GB-s - included 400,000 GB-s) x $12.50 / 1,000,000 = $0.00
+{ props.product === "durable-objects" && <>> }
+{ props.product === "workers" && <>> }
-#### Example 2
+
-If 100 Durable Objects each had 100 WebSocket connections established to each of them which sent approximately one message a minute for a month, the estimated cost in a month would be, if the messages overlapped so that the Objects were actually active for half the month:
+| | Workers Free plan | Workers Paid plan |
+| ----------------------------- | ------------------------- | --------------------------------------------------------- |
+| Rows reads 1,2 | 5 million / day | First 25 billion / month included + $0.001 / million rows |
+| Rows written 1,2,3,4 | 100,000 / day | First 50 million / month included + $1.00 / million rows |
+| SQL Stored data 5 | 5 GB (total) | 5 GB-month, + $0.20/ GB-month |
-Total = ~$64.65 USD + $202.36 USD + Minimum $5/mo usage = $272.01
+
-- 100 requests to establish the WebSockets.
-- 100 messages per minute \* 100 Durable Objects \* 60 minutes \* 24 hours \* 30 days = 432,000,000 requests
-- (432 million requests - included 1 million requests) x $0.15 / 1,000,000 = $64.65
-- 100 Durable Objects \* 60 seconds \* 60 minutes \* 24 hours \* 30 days / 2 = 129,600,000 seconds
-- 129,600,000 seconds \* 128 MB / 1 GB = 16,588,800 GB-s
-- (16,588,800 GB-s - included 400,000 GB-s) x $12.50 / 1,000,000 = $202.36
+1 Rows read and rows written included limits and rates match [D1 pricing](/d1/platform/pricing/), Cloudflare's serverless SQL database.
-#### Example 3
+2 Key-value methods like `get()`, `put()`, `delete()`, or `list()` store and query data in a hidden SQLite table and are billed as rows read and rows written.
-If 100 Durable Objects each had a single WebSocket connection established to each of them, which sent one message a second for a month, and the messages overlapped so that the Objects were actually active for the entire month, the estimated cost in a month would be:
+3 Each `setAlarm()` is billed as a single row written.
-Total = ~$38.73 USD + $409.72 USD + Minimum $5/mo usage = $453.45
+4 Deletes are counted as rows written.
-- 100 requests to establish the WebSockets.
-- 1 message per second \* 100 connections \* 60 seconds \* 60 minutes \* 24 hours \* 30 days = 259,200,000 requests
-- (259.2 million requests - included 1 million requests) x $0.15 / 1,000,000 = $38.73
-- 100 Durable Objects \* 60 seconds \* 60 minutes \* 24 hours \* 30 days = 259,200,000 seconds
-- 259,200,000 seconds \* 128 MB / 1 GB = 33,177,600 GB-s
-- (33,177,600 GB-s - included 400,000 GB-s) x $12.50 / 1,000,000 = $409.72
+5 Durable Objects will be billed for stored data until the [data is removed](/durable-objects/best-practices/access-durable-objects-storage/#remove-a-durable-objects-storage). Once the data is removed, the object will be cleaned up automatically by the system.
-## Storage API
+
-The [Storage API](/durable-objects/api/storage-api/) is only accessible from within Durable Objects. Durable Objects do not have to use the Storage API, but if your code does call methods on `state.storage`, it will incur the following additional charges:
+{ props.product === "durable-objects" && <>> }
+{ props.product === "workers" && <>> }
-| | Paid plan |
+| | Workers Paid plan |
| -------------------------------- | -------------------------- |
| Read request units1,2 | 1 million, + $0.20/million |
-| Write request units1 | 1 million, + $1.00/million |
-| Delete requests3 | 1 million, + $1.00/million |
-| Stored data4 | 1 GB, + $0.20/ GB-month |
+| Write request units3 | 1 million, + $1.00/million |
+| Delete requests4 | 1 million, + $1.00/million |
+| Stored data5 | 1 GB, + $0.20/ GB-month |
+
+
+
+1 A request unit is defined as 4 KB of data read or written. A request that writes or reads more than 4 KB will consume multiple units, for example, a 9 KB write will consume 3 write request units.
+
+2 List operations are billed by read request units, based on the amount of data examined. For example, a list request that returns a combined 80 KB of keys and values will be billed 20 read request units. A list request that does not return anything is billed for 1 read request unit.
+
+3 Each `setAlarm` is billed as a single write request unit.
+
+4 Delete requests are unmetered. For example, deleting a 100 KB value will be charged one delete request.
+
+5 Durable Objects will be billed for stored data until the data is removed. Once the data is removed, the object will be cleaned up automatically by the system.
-1. A request unit is defined as 4 KB of data read or written. A request that writes or reads more than 4 KB will consume multiple units. For example, a 9 KB write will consume three write request units.
-2. List operations are billed by read request units, based on the amount of data examined. For example, a list request that returns a combined 80 KB of keys and values will be billed 20 read request units. A list request that does not return anything is billed for one read request unit.
-3. Delete requests are unmetered. For example, deleting a 100 KB value will be charged one delete request.
-4. Objects will be billed for stored data until the data is removed. Once the data is removed, the object will be cleaned up automatically by the system.
-5. Each alarm write is billed as a single write request unit.
+Requests that hit the [Durable Objects in-memory cache](/durable-objects/reference/in-memory-state/) or that use the [multi-key versions of `get()`/`put()`/`delete()` methods](/durable-objects/api/storage-api/) are billed the same as if they were a normal, individual request for each key.
-Requests that hit the Durable Objects in-memory cache or that use the [multi-key versions of `get()`/`put()`/`delete()` methods](/durable-objects/api/storage-api/) are billed the same as if they were a normal, individual request for each key.
+
\ No newline at end of file
diff --git a/src/content/partials/durable-objects/durable-objects-sql.mdx b/src/content/partials/durable-objects/durable-objects-sql.mdx
index c00523582d61ccf..2e3290f457cbbba 100644
--- a/src/content/partials/durable-objects/durable-objects-sql.mdx
+++ b/src/content/partials/durable-objects/durable-objects-sql.mdx
@@ -4,7 +4,7 @@
import { Details } from "~/components"
-[SQL API](/durable-objects/api/sql-storage/#exec) examples below use the following SQL schema:
+[SQL API](/durable-objects/api/storage-api/#exec) examples below use the following SQL schema:
```ts
import { DurableObject } from "cloudflare:workers";
diff --git a/src/content/partials/durable-objects/durable-objects-vs-d1.mdx b/src/content/partials/durable-objects/durable-objects-vs-d1.mdx
index dd23ebb36a3bfd6..ed46119b57d1244 100644
--- a/src/content/partials/durable-objects/durable-objects-vs-d1.mdx
+++ b/src/content/partials/durable-objects/durable-objects-vs-d1.mdx
@@ -4,7 +4,7 @@
## SQL in Durable Objects vs D1
-Cloudflare Workers offers a SQLite-backed serverless database product - [D1](/d1/). How should you compare [SQLite in Durable Objects](/durable-objects/best-practices/access-durable-objects-storage/#sql-storage) and D1?
+Cloudflare Workers offers a SQLite-backed serverless database product - [D1](/d1/). How should you compare [SQLite in Durable Objects](/durable-objects/best-practices/access-durable-objects-storage/) and D1?
**D1 is a managed database product.**
@@ -22,4 +22,4 @@ Durable Objects require a bit more effort, but in return, give you more flexibil
With SQLite in Durable Objects, you may also need to build some of your own database tooling that comes out-of-the-box with D1.
-SQL query pricing and limits are intended to be identical between D1 ([pricing](/d1/platform/pricing/), [limits](/d1/platform/limits/)) and SQLite in Durable Objects ([pricing](/durable-objects/platform/pricing/#sql-storage-billing), [limits](/durable-objects/platform/limits/)). During SQLite in Durable Objects beta, Storage per Durable Object is 1GB, which will be raised to mirror storage per D1 database (10GB) by general availability.
\ No newline at end of file
+SQL query pricing and limits are intended to be identical between D1 ([pricing](/d1/platform/pricing/), [limits](/d1/platform/limits/)) and SQLite in Durable Objects ([pricing](/durable-objects/platform/pricing/#sql-storage-billing), [limits](/durable-objects/platform/limits/)).
\ No newline at end of file
diff --git a/src/content/partials/durable-objects/storage-intro-text.mdx b/src/content/partials/durable-objects/storage-intro-text.mdx
new file mode 100644
index 000000000000000..7b9d9744dd0391f
--- /dev/null
+++ b/src/content/partials/durable-objects/storage-intro-text.mdx
@@ -0,0 +1,23 @@
+---
+{}
+---
+
+The Durable Object Storage API comes with several methods, including [SQL API](/durable-objects/api/storage-api/#sql-api), [point-in-time-recovery (PITR) API](/durable-objects/api/storage-api/#pitr-point-in-time-recovery-api), and [key-value (KV) API](/durable-objects/api/storage-api/#kv-api).
+
+- Durable Object classes with the recommended, [SQLite storage backend](/durable-objects/best-practices/access-durable-objects-storage/#sqlite-storage-backend) can use SQL API, PITR API, and KV API. KV API methods like `get()`, `put()`, `delete()`, or `list()` store data in a hidden SQLite table.
+- Specifically for Durable Object classes with SQLite storage backend, KV operations which were previously asynchronous (for example, [`get`](/durable-objects/api/storage-api/#get), [`put`](/durable-objects/api/storage-api/#put), [`delete`](/durable-objects/api/storage-api/#delete), [`deleteAll`](/durable-objects/api/storage-api/#deleteall), [`list`](/durable-objects/api/storage-api/#list)) are synchronous, even though they return promises. These methods will have completed their operations before they return the promise.
+- Durable Objects gain access to a persistent KV Durable Object Storage API via the `DurableObjectStorage` interface and accessed by the `DurableObjectState::storage` property. This is frequently accessed via `this.ctx.storage` when the `ctx` parameter passed to the Durable Object constructor.
+- Durable Object classes with the key-value storage backend can only use KV API.
+
+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.
+
+| Type of storage API | SQLite-backed Durable Object | KV-backed Durable Object |
+| ------------------- | ---------------------------- | ------------------------ |
+| SQL API | ✅ | ❌ |
+| PITR API | ✅ | ❌ |
+| KV API | ✅ | ✅ |
+| Alarms API | ✅ | ✅ |
+
+:::note[Recommended SQLite-backed Durable Object]
+We recommend using SQLite-backed Durable Object over key-value backed Durable Object. SQLite-backed Durable Objects allow you to store more types of data (such as tables), and offers Point In Time Recovery API which can restore a the Durable Object's embedded SQLite database contents (both SQL data and key-value data) to any point in the past 30 days.
+:::
\ No newline at end of file
diff --git a/src/content/partials/workers/durable_objects_pricing.mdx b/src/content/partials/workers/durable_objects_pricing.mdx
deleted file mode 100644
index 4ec8cc7d82a8a76..000000000000000
--- a/src/content/partials/workers/durable_objects_pricing.mdx
+++ /dev/null
@@ -1,31 +0,0 @@
----
-{}
----
-
-import { Markdown, GlossaryTooltip } from "~/components";
-
-[Durable Objects](/durable-objects/) are only available on the [Workers Paid plan](/workers/platform/pricing/#workers), and are billed while the Durable Object is active (including the request context).
-
-| | Paid plan |
-| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
-| Requests | 1 million, + $0.15/million
Includes HTTP requests, RPC sessions1, WebSocket messages2, and alarm invocations |
-| Duration3 | 400,000 GB-s, + $12.50/million GB-s4,5 |
-
-1 Each [RPC session](/workers/runtime-apis/rpc/lifecycle/) is billed as one request to your Durable Object. Every [RPC method call](/durable-objects/best-practices/create-durable-object-stubs-and-send-requests/) on a [Durable Objects stub](/durable-objects/) is its own RPC session and therefore a single billed request.
-
-RPC method calls can return objects (stubs) extending [`RpcTarget`](/workers/runtime-apis/rpc/lifecycle/#lifetimes-memory-and-resource-management) and invoke calls on those stubs. Subsequent calls on the returned stub are part of the same RPC session and are not billed as separate requests. For example:
-
-```js
-let durableObjectStub = OBJECT_NAMESPACE.get(id); // retrieve Durable Object stub
-using foo = await durableObjectStub.bar(); // billed as a request
-await foo.baz(); // treated as part of the same RPC session created by calling bar(), not billed as a request
-await durableObjectStub.cat(); // billed as a request
-```
-
-2 A request is needed to create a WebSocket connection. There is no charge for outgoing WebSocket messages, nor for incoming [WebSocket protocol pings](https://www.rfc-editor.org/rfc/rfc6455#section-5.5.2). For compute requests billing-only, a 20:1 ratio is applied to incoming WebSocket messages to factor in smaller messages for real-time communication. For example, 100 WebSocket incoming messages would be charged as 5 requests for billing purposes. The 20:1 ratio does not affect Durable Object metrics and analytics, which reflect actual usage.
-
-3 Application-level auto-response messages handled by [`state.setWebSocketAutoResponse()`](/durable-objects/api/state/#setwebsocketautoresponse) will not incur additional wall-clock time, and so they will not be charged.
-
-4 Duration is billed in wall-clock time as long as the Object is active, but is shared across all requests active on an Object at once. Calling `accept()` on a WebSocket in an Object will incur duration charges for the entire time the WebSocket is connected. Note that the request context for a Durable Object extends at least 60 seconds after the last client disconnects. If you prefer, use the [WebSocket hibernation API](/durable-objects/best-practices/websockets/#websocket-hibernation-api) to avoid incurring duration charges once all event handlers finish running.
-
-5 Duration billing charges for the 128 MB of memory your Durable Object is allocated, regardless of actual usage. If your account creates many instances of a single Durable Object class, Durable Objects may run in the same isolate on the same physical machine and share the 128 MB of memory. These Durable Objects are still billed as if they are allocated a full 128 MB of memory.
diff --git a/src/content/partials/workers/storage_api_pricing.mdx b/src/content/partials/workers/storage_api_pricing.mdx
deleted file mode 100644
index c6ca74a5222be30..000000000000000
--- a/src/content/partials/workers/storage_api_pricing.mdx
+++ /dev/null
@@ -1,57 +0,0 @@
----
-{}
-
----
-
-The Durable Objects [Storage API](/durable-objects/api/storage-api) is only accessible from within Durable Objects.
-
-Durable Objects do not have to use the Storage API, but if your code does call methods on `ctx.storage`, it will incur additional charges.
-
-Pricing depends on whether a Durable Object class has default, key-value storage backend or opt-in [SQLite storage backend](/durable-objects/best-practices/access-durable-objects-storage/#sqlite-storage-backend).
-
-### Key-value storage backend
-
-| | Workers Paid plan |
-| -------------------------------- | -------------------------- |
-| Read request units1,2 | 1 million, + $0.20/million |
-| Write request units3 | 1 million, + $1.00/million |
-| Delete requests4 | 1 million, + $1.00/million |
-| Stored data5 | 1 GB, + $0.20/ GB-month |
-
-1 A request unit is defined as 4 KB of data read or written. A request that writes or reads more than 4 KB will consume multiple units, for example, a 9 KB write will consume 3 write request units.
-
-2 List operations are billed by read request units, based on the amount of data examined. For example, a list request that returns a combined 80 KB of keys and values will be billed 20 read request units. A list request that does not return anything is billed for 1 read request unit.
-
-3 Each `setAlarm` is billed as a single write request unit.
-
-4 Delete requests are unmetered. For example, deleting a 100 KB value will be charged one delete request.
-
-5 Durable Objects will be billed for stored data until the data is removed. Once the data is removed, the object will be cleaned up automatically by the system.
-
-Requests that hit the [Durable Objects in-memory cache](/durable-objects/reference/in-memory-state/) or that use the [multi-key versions of `get()`/`put()`/`delete()` methods](/durable-objects/api/storage-api/) are billed the same as if they were a normal, individual request for each key.
-
-### SQLite storage backend
-
-:::note[SQLite in Durable Objects Beta]
-
-The new beta version of Durable Objects is available where each Durable Object has a private, embedded SQLite database. When deploying a new Durable Object class, users can [opt-in to a SQLite storage backend](/durable-objects/best-practices/access-durable-objects-storage/#sqlite-storage-backend) in order to access new [SQL API](/durable-objects/api/sql-storage/#exec).
-
-During the initial beta, Storage API billing is not enabled for Durable Object classes using SQLite storage backend. SQLite-backed Durable Objects will incur [charges for requests and duration](/durable-objects/platform/pricing/#billing-metrics). We plan to enable Storage API billing for Durable Objects using SQLite storage backend in the first half of 2025 after advance notice with the below pricing.
-
-You can introspect rows read and rows written using `cursor.rowsRead` and `cursor.rowsWritten` on the [cursor returned](/durable-objects/api/storage-api/) by a SQL query and using [GraphQL analytics](/durable-objects/observability/graphql-analytics/#query-via-the-graphql-api). [`ctx.storage.sql.databaseSize`](/durable-objects/api/sql-storage/#databasesize) returns the current SQL database size for a Durable Object.
-
-:::
-
-For [Durable Objects classes with SQLite storage backend](/durable-objects/reference/durable-objects-migrations/#enable-sqlite-storage-backend-on-new-durable-object-class-migration) via `ctx.storage.sql` the following pricing is used instead:
-
-| | Workers Paid plan |
-| ----------------------------| -------------------------- |
-| Rows reads 1,2 | First 25 billion / month included + $0.001 / million rows |
-| Rows written 1,2 | First 50 million / month included + $1.00 / million rows |
-| SQL Stored data | 5 GB-month, + $0.20/ GB-month |
-
-1 Rows read and rows written included limits and rates match D1, Cloudflare's serverless SQL database, [pricing](/d1/platform/pricing/).
-
-2 Key-value methods like `get()`, `put()`, `delete()`, or `list()` store and query data in a hidden SQLite table and are billed as rows read and rows written.
-
-3 Each `setAlarm` is billed as a single row written.
diff --git a/src/content/release-notes/durable-objects.yaml b/src/content/release-notes/durable-objects.yaml
index f85851fa0797d88..2732c6bd312ecee 100644
--- a/src/content/release-notes/durable-objects.yaml
+++ b/src/content/release-notes/durable-objects.yaml
@@ -5,6 +5,18 @@ productLink: "/durable-objects/"
productArea: Developer platform
productAreaLink: /workers/platform/changelog/platform/
entries:
+ - publish_date: "2025-04-07"
+ title: Durable Objects on Workers Free plan
+ description: |-
+ [SQLite-backed Durable Objects](/durable-objects/get-started/) are now available on the Workers Free plan with these [limits](/durable-objects/platform/pricing/).
+
+ - publish_date: "2025-04-07"
+ title: SQLite in Durable Objects GA
+ description: |-
+ [SQLite-backed Durable Objects](/durable-objects/best-practices/access-durable-objects-storage/#create-sqlite-backed-durable-object-class) and corresponding [Storage API](/durable-objects/api/storage-api/) methods like `sql.exec` have moved from beta to general availability. New Durable Object classes should use wrangler configuration for SQLite storage over key-value storage.
+
+ SQLite storage per Durable Object has increased to 10GB for all existing and new objects.
+
- publish_date: "2025-02-19"
description: |-
SQLite-backed Durable Objects now support `PRAGMA optimize` command, which can improve database query performance. It is recommended to run this command after a schema change (for example, after creating an index). Refer to [`PRAGMA optimize`](/d1/sql-api/sql-statements/#pragma-optimize) for more information.
@@ -26,7 +38,7 @@ entries:
- publish_date: "2024-09-26"
title: (Beta) SQLite storage backend & SQL API available on new Durable Object classes
description: |-
- The new beta version of Durable Objects is available where each Durable Object has a private, embedded SQLite database. When deploying a new Durable Object class, users can [opt-in to a SQLite storage backend](/durable-objects/best-practices/access-durable-objects-storage/#sqlite-storage-backend) in order to access new [SQL API](/durable-objects/api/sql-storage/#exec) and [point-in-time-recovery API](/durable-objects/api/sql-storage/#point-in-time-recovery), part of Durable Objects Storage API.
+ The new beta version of Durable Objects is available where each Durable Object has a private, embedded SQLite database. When deploying a new Durable Object class, users can [opt-in to a SQLite storage backend](/durable-objects/best-practices/access-durable-objects-storage/#sqlite-storage-backend) in order to access new [SQL API](/durable-objects/api/storage-api/#sql-api) and [point-in-time-recovery API](/durable-objects/api/storage-api/#pitr-point-in-time-recovery-api), part of Durable Objects Storage API.
You cannot enable a SQLite storage backend on an existing, deployed Durable Object class. Automatic migration of deployed classes from their key-value storage backend to SQLite storage backend will be available in the future.
diff --git a/src/content/release-notes/hyperdrive.yaml b/src/content/release-notes/hyperdrive.yaml
index e9f350221628117..e8dbc758cef23ad 100644
--- a/src/content/release-notes/hyperdrive.yaml
+++ b/src/content/release-notes/hyperdrive.yaml
@@ -90,6 +90,6 @@ entries:
- publish_date: "2023-09-28"
title: Hyperdrive now available
description: |-
- Hyperdrive is now available in public beta to any developer with a Workers paid plan.
+ Hyperdrive is now available in public beta to any developer with a Workers Paid plan.
To start using Hyperdrive, visit the [get started](/hyperdrive/get-started/) guide or read the [announcement blog](https://blog.cloudflare.com/hyperdrive-making-regional-databases-feel-distributed/) to learn more.