Skip to content

Commit 8f4f101

Browse files
Oxyjunlambrospetroupedrosousa
authored
[DO] Updating Durable Objects storage API docs (#24964)
* Splitting Storage API chapter into two * Fixing links * Fixing broken links * Apply suggestions from code review Co-authored-by: Lambros Petrou <[email protected]> * Implementing feedback * Apply suggestions from code review Co-authored-by: Lambros Petrou <[email protected]> * Apply suggestions from code review Co-authored-by: Pedro Sousa <[email protected]> * Using spaces instead of tabs * Using anchorheadings and self-referencing links in partial * Update src/content/partials/durable-objects/api-sync-kv.mdx Co-authored-by: Lambros Petrou <[email protected]> * Update src/content/partials/durable-objects/api-storage-introduction.mdx Co-authored-by: Lambros Petrou <[email protected]> * Update src/content/partials/durable-objects/api-storage-introduction.mdx Co-authored-by: Lambros Petrou <[email protected]> --------- Co-authored-by: Lambros Petrou <[email protected]> Co-authored-by: Pedro Sousa <[email protected]>
1 parent f884dd6 commit 8f4f101

30 files changed

+572
-438
lines changed

public/__redirects

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@
532532
/durable-objects/api/hibernatable-websockets-api/ /durable-objects/best-practices/websockets/ 301
533533
/durable-objects/api/alarms-in-durable-objects/ /durable-objects/api/alarms/ 301
534534
/durable-objects/api/websockets/ /durable-objects/best-practices/websockets/ 301
535-
/durable-objects/api/sql-storage/ /durable-objects/api/storage-api/ 301
535+
/durable-objects/api/sql-storage/ /durable-objects/api/sqlite-storage-api/ 301
536536
/durable-objects/platform/data-location/ /durable-objects/reference/data-location/ 301
537537
/durable-objects/platform/environments/ /durable-objects/reference/environments/ 301
538538
/durable-objects/platform/troubleshooting/ /durable-objects/observability/troubleshooting/ 301
@@ -547,7 +547,7 @@
547547
/durable-objects/reference/error-handling/ /durable-objects/best-practices/error-handling/ 301
548548
/durable-objects/reference/troubleshooting/ /durable-objects/observability/troubleshooting/ 301
549549
/durable-objects/reference/websockets/ /durable-objects/best-practices/websockets/ 301
550-
/durable-objects/api/transactional-storage-api/ /durable-objects/api/storage-api/ 301
550+
/durable-objects/api/transactional-storage-api/ /durable-objects/api/sqlite-storage-api/ 301
551551
/durable-objects/platform/changelog/ /durable-objects/release-notes/ 301
552552
/durable-objects/changelog/ /durable-objects/release-notes/ 301
553553
/durable-objects/glossary/ /durable-objects/reference/glossary/ 301

src/content/changelog/durable-objects/2025-04-07-sqlite-in-durable-objects-ga.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ date: 2025-04-07
99

1010
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.
1111

12-
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.
12+
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/sqlite-storage-api/#sql-api) and [point-in-time recovery](/durable-objects/api/sqlite-storage-api/#pitr-point-in-time-recovery-api) methods, which provide relational data modeling, SQL querying, and better data management.
1313

1414
```js
1515
export class MyDurableObject extends DurableObject {

src/content/docs/agents/api-reference/schedule-tasks.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ Calling `await this.schedule` returns a `Schedule`, which includes the task's ra
7070

7171
:::note[Maximum scheduled tasks]
7272

73-
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).
73+
Each task is mapped to a row in the Agent's underlying [SQLite database](/durable-objects/api/sqlite-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).
7474

7575
:::
7676

src/content/docs/agents/api-reference/store-and-sync-state.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ Learn more about the zero-latency SQL storage that powers both Agents and Durabl
227227

228228
:::
229229

230-
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/).
230+
The SQL API exposed to an Agent is similar to the one [within Durable Objects](/durable-objects/api/sqlite-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/).
231231

232232
### Use Agent state as model context
233233

src/content/docs/durable-objects/api/alarms.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,4 @@ class MyDurableObject extends DurableObject {
133133
134134
- Understand how to [use the Alarms API](/durable-objects/examples/alarms-api/) in an end-to-end example.
135135
- Read the [Durable Objects alarms announcement blog post](https://blog.cloudflare.com/durable-objects-alarms/).
136-
- Review the [Storage API](/durable-objects/api/storage-api/) documentation for Durable Objects.
136+
- Review the [Storage API](/durable-objects/api/sqlite-storage-api/) documentation for Durable Objects.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
---
2+
title: KV-backed Durable Object Storage (Legacy)
3+
pcx_content_type: concept
4+
sidebar:
5+
order: 7
6+
---
7+
8+
import { Render, Type, MetaInfo, GlossaryTooltip, TypeScriptExample, Details } from "~/components";
9+
10+
:::note
11+
This page documents the storage API for legacy KV-backed Durable Objects.
12+
13+
For the newer SQLite-backed Durable Object storage API, refer to [SQLite-backed Durable Object Storage](/durable-objects/api/sqlite-storage-api).
14+
:::
15+
16+
<Render file="api-storage-introduction" product="durable-objects" />
17+
18+
<Render file="recommend-sqlite-do" product="durable-objects" />
19+
20+
## Access storage
21+
22+
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.
23+
24+
The following code snippet shows you how to store and retrieve data using the Durable Object Storage API.
25+
26+
<TypeScriptExample>
27+
```ts
28+
export class Counter extends DurableObject {
29+
constructor(ctx: DurableObjectState, env: Env) {
30+
super(ctx, env);
31+
}
32+
33+
async increment(): Promise<number> {
34+
let value: number = (await this.ctx.storage.get("value")) || 0;
35+
value += 1;
36+
await this.ctx.storage.put("value", value);
37+
return value;
38+
}
39+
}
40+
```
41+
</TypeScriptExample>
42+
43+
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 <GlossaryTooltip term="input gate">input gates</GlossaryTooltip> and <GlossaryTooltip term="output gate">output gates</GlossaryTooltip> 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/).
44+
45+
## Asynchronous KV API
46+
47+
KV-backed Durable Objects provide KV API methods which are asynchronous.
48+
49+
<Render file="api-async-kv-legacy" product="durable-objects" />
50+
51+
## Alarms
52+
53+
<Render file="api-storage-alarms" product="durable-objects" />
54+
55+
## Other
56+
57+
<Render file="api-storage-other-methods" product="durable-objects" />
58+
59+
## Related resources
60+
61+
- [Durable Objects: Easy, Fast, Correct Choose Three](https://blog.cloudflare.com/durable-objects-easy-fast-correct-choose-three/)
62+
- [Zero-latency SQLite storage in every Durable Object blog](https://blog.cloudflare.com/sqlite-in-durable-objects/)
63+
- [WebSockets API](/durable-objects/best-practices/websockets/)
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
---
2+
title: SQLite-backed Durable Object Storage
3+
pcx_content_type: concept
4+
sidebar:
5+
order: 6
6+
---
7+
8+
import { Render, Type, MetaInfo, GlossaryTooltip, TypeScriptExample, Details } from "~/components";
9+
10+
:::note
11+
This page documents the storage API for the newer SQLite-backed Durable Objects.
12+
13+
For the legacy KV-backed Durable Object storage API, refer to [KV-backed Durable Object Storage (Legacy)](/durable-objects/api/legacy-kv-storage-api/).
14+
:::
15+
16+
<Render file="api-storage-introduction" product="durable-objects" />
17+
18+
<Render file="recommend-sqlite-do" product="durable-objects" />
19+
20+
<Render file="do-sqlite-storage-no-bill-note" product="durable-objects" />
21+
22+
## Access storage
23+
24+
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.
25+
26+
The following code snippet shows you how to store and retrieve data using the Durable Object Storage API.
27+
28+
<TypeScriptExample>
29+
```ts
30+
export class Counter extends DurableObject {
31+
constructor(ctx: DurableObjectState, env: Env) {
32+
super(ctx, env);
33+
}
34+
35+
async increment(): Promise<number> {
36+
let value: number = (await this.ctx.storage.get('value')) || 0;
37+
value += 1;
38+
await this.ctx.storage.put('value', value);
39+
return value;
40+
}
41+
42+
}
43+
44+
```
45+
</TypeScriptExample>
46+
47+
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 <GlossaryTooltip term="input gate">input gates</GlossaryTooltip> and <GlossaryTooltip term="output gate">output gates</GlossaryTooltip> 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/).
48+
49+
## SQL API
50+
51+
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/sqlite-storage-api/#sql) of `DurableObjectStorage` class.
52+
53+
For example, using `sql.exec()` a user can create a table and insert rows.
54+
55+
```ts
56+
import { DurableObject } from "cloudflare:workers";
57+
58+
export class MyDurableObject extends DurableObject {
59+
sql: SqlStorage;
60+
constructor(ctx: DurableObjectState, env: Env) {
61+
super(ctx, env);
62+
this.sql = ctx.storage.sql;
63+
64+
this.sql.exec(`
65+
CREATE TABLE IF NOT EXISTS artist(
66+
artistid INTEGER PRIMARY KEY,
67+
artistname TEXT
68+
);
69+
INSERT INTO artist (artistid, artistname) VALUES
70+
(123, 'Alice'),
71+
(456, 'Bob'),
72+
(789, 'Charlie');
73+
`);
74+
}
75+
}
76+
```
77+
78+
- 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 KV-storage backend.
79+
- When writing data, every row update of an 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).
80+
- Writing data to [SQLite virtual tables](https://www.sqlite.org/vtab.html) also counts towards rows written.
81+
82+
<Render file="d1-do-supported-sqlite-extensions" product="d1" params={{ product: "Durable Objects support" }}/>
83+
84+
### `exec`
85+
86+
<code>exec(query: <Type text="string" />, ...bindings: <Type text="any[]" />)</code>: <Type text="SqlStorageCursor" />
87+
88+
#### Parameters
89+
90+
- `query`: <Type text ='string' />
91+
- 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.
92+
- `...bindings`: <Type text='any[]' /> <MetaInfo text='Optional' />
93+
- Optional variable number of arguments that correspond to the `?` placeholders in `query`.
94+
95+
#### Returns
96+
97+
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()`.
98+
99+
`SqlStorageCursor` supports the following methods:
100+
101+
- `next()`
102+
- 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.
103+
- `toArray()`
104+
- Iterates through remaining cursor value(s) and returns an array of returned row objects.
105+
- `one()`
106+
- 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.
107+
- `raw()`: <Type text='Iterator' />
108+
- 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.
109+
- Returned Iterator supports `next()` and `toArray()` methods above.
110+
- Returned cursor and `raw()` iterator iterate over the same query results and can be combined. For example:
111+
112+
```ts
113+
let cursor = this.sql.exec("SELECT * FROM artist ORDER BY artistname ASC;");
114+
let rawResult = cursor.raw().next();
115+
116+
if (!rawResult.done) {
117+
console.log(rawResult.value); // prints [ 123, 'Alice' ]
118+
} else {
119+
// query returned zero results
120+
}
121+
122+
console.log(cursor.toArray()); // prints [{ artistid: 456, artistname: 'Bob' },{ artistid: 789, artistname: 'Charlie' }]
123+
```
124+
125+
`SqlStorageCursor` has the following properties:
126+
127+
- `columnNames`: <Type text='string[]' />
128+
- The column names of the query in the order they appear in each row array returned by the `raw` iterator.
129+
- `rowsRead`: <Type text='number' />
130+
- 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).
131+
- `rowsWritten`: <Type text='number' />
132+
- 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).
133+
- Any numeric value in a column is affected by JavaScript's 52-bit precision for numbers. If you store a very large number (in `int64`), then retrieve the same value, the returned value may be less precise than your original number.
134+
135+
:::note[SQL transactions]
136+
Note that `sql.exec()` cannot execute transaction-related statements like `BEGIN TRANSACTION` or `SAVEPOINT`. Instead, use the [`ctx.storage.transaction()`](/durable-objects/api/sqlite-storage-api/#transaction) or [`ctx.storage.transactionSync()`](/durable-objects/api/sqlite-storage-api/#transactionsync) APIs to start a transaction, and then execute SQL queries in your callback.
137+
:::
138+
139+
#### Examples
140+
141+
<Render file="durable-objects-sql" product="durable-objects" />
142+
143+
### `databaseSize`
144+
145+
`databaseSize`: <Type text ='number' />
146+
147+
#### Returns
148+
149+
The current SQLite database size in bytes.
150+
151+
```ts
152+
let size = ctx.storage.sql.databaseSize;
153+
```
154+
155+
## PITR (Point In Time Recovery) API
156+
157+
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.
158+
159+
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.
160+
161+
### `getCurrentBookmark`
162+
163+
<code>ctx.storage.getCurrentBookmark()</code>: <Type text="Promise<string>" />
164+
165+
- Returns a bookmark representing the current point in time in the object's history.
166+
167+
### `getBookmarkForTime`
168+
169+
<code>ctx.storage.getBookmarkForTime(timestamp: <Type text="number | Date" />)</code>: <Type text="Promise<string>" />
170+
171+
- 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)`.
172+
173+
### `onNextSessionRestoreBookmark`
174+
175+
<code>ctx.storage.onNextSessionRestoreBookmark(bookmark: <Type text="string" />)</code>: <Type text="Promise<string>" />
176+
177+
- 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.
178+
179+
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.
180+
181+
```ts
182+
let now = new Date();
183+
// restore to 2 days ago
184+
let bookmark = ctx.storage.getBookmarkForTime(now - 2);
185+
ctx.storage.onNextSessionRestoreBookmark(bookmark);
186+
```
187+
188+
## Synchronous KV API
189+
190+
<Render file="api-sync-kv" product="durable-objects" />
191+
192+
## Alarms
193+
194+
<Render file="api-storage-alarms" product="durable-objects" />
195+
196+
## Other
197+
198+
<Render file="api-storage-other-methods" product="durable-objects" />
199+
200+
## Storage properties
201+
202+
### `sql`
203+
204+
`sql` is a readonly property of type `DurableObjectStorage` encapsulating the [SQL API](/durable-objects/api/sqlite-storage-api/#synchronous-sql-api).
205+
206+
## Related resources
207+
208+
- [Durable Objects: Easy, Fast, Correct Choose Three](https://blog.cloudflare.com/durable-objects-easy-fast-correct-choose-three/)
209+
- [Zero-latency SQLite storage in every Durable Object blog](https://blog.cloudflare.com/sqlite-in-durable-objects/)
210+
- [WebSockets API](/durable-objects/best-practices/websockets/)

0 commit comments

Comments
 (0)