6.0 KiB
Durable Objects State API Reference
Complete reference for the State API (SQL and Key-Value storage).
SQL API (SQLite Backend)
Access via ctx.storage.sql (requires SQLite backend in migration).
exec(query, ...params)
Execute SQL query with optional parameters. Returns cursor.
// Insert with RETURNING
const cursor = this.sql.exec(
'INSERT INTO users (name, email) VALUES (?, ?) RETURNING id',
'Alice',
'alice@example.com'
);
// SELECT
const cursor = this.sql.exec('SELECT * FROM users WHERE id = ?', userId);
// UPDATE
this.sql.exec('UPDATE users SET email = ? WHERE id = ?', newEmail, userId);
// DELETE
this.sql.exec('DELETE FROM users WHERE id = ?', userId);
Parameters:
query(string): SQL query with?placeholders...params(any[]): Values to bind to placeholders
Returns: SqlCursor
Cursor Methods
// Get single row (throws if 0 or >1 rows)
const row = cursor.one<{ id: number; name: string }>();
// Get single row (returns null if no rows)
const row = cursor.one<RowType>({ allowNone: true });
// Get all rows as array
const rows = cursor.toArray<RowType>();
// Iterate cursor
for (const row of cursor) {
console.log(row.name);
}
Transactions (Synchronous)
this.ctx.storage.transactionSync(() => {
this.sql.exec('INSERT INTO table1 ...');
this.sql.exec('UPDATE table2 ...');
// All or nothing - atomic
});
CRITICAL: Must be synchronous (no async/await inside).
Key-Value API
Available on both SQLite and KV backends via ctx.storage.
get(key) / get(keys[])
Get single or multiple values.
// Get single value
const value = await this.ctx.storage.get<number>('count');
// Get multiple values (returns Map)
const map = await this.ctx.storage.get<string>(['key1', 'key2', 'key3']);
// Iterate Map
for (const [key, value] of map.entries()) {
console.log(key, value);
}
Parameters:
key(string): Key to retrievekeys(string[]): Array of keys to retrieve
Returns: Promise or Promise<Map<string, value>>
put(key, value) / put(entries)
Put single or multiple values.
// Put single value
await this.ctx.storage.put('count', 42);
// Put multiple values
await this.ctx.storage.put({
key1: 'value1',
key2: 'value2',
key3: 'value3',
});
Parameters:
key(string): Key to storevalue(any): Value to store (must be serializable)entries(Record<string, any>): Object with key-value pairs
Returns: Promise
delete(key) / delete(keys[])
Delete single or multiple keys.
// Delete single key
await this.ctx.storage.delete('key1');
// Delete multiple keys
await this.ctx.storage.delete(['key1', 'key2', 'key3']);
Returns: Promise (true if deleted)
list(options)
List keys with optional filtering.
// List all keys
const map = await this.ctx.storage.list();
// List with prefix
const map = await this.ctx.storage.list({ prefix: 'user:' });
// List with limit
const map = await this.ctx.storage.list({ limit: 100 });
// List in reverse order
const map = await this.ctx.storage.list({ reverse: true });
// List with start/end range
const map = await this.ctx.storage.list({
start: 'user:a',
end: 'user:z',
});
Parameters:
prefix(string): Filter keys by prefixlimit(number): Max keys to returnreverse(boolean): Reverse orderstart(string): Start key (inclusive)end(string): End key (exclusive)
Returns: Promise<Map<string, any>>
deleteAll()
Delete all storage (DO will cease to exist after shutdown).
// Delete alarm first
await this.ctx.storage.deleteAlarm();
// Delete all storage
await this.ctx.storage.deleteAll();
CRITICAL:
- ✅ Atomic on SQLite backend
- ⚠️ May be partial on KV backend
Returns: Promise
transaction(callback)
Async transaction for KV operations.
await this.ctx.storage.transaction(async (txn) => {
const value = await txn.get('count');
await txn.put('count', value + 1);
await txn.put('lastUpdate', Date.now());
// All or nothing
});
Returns: Promise (callback return value)
Alarms API
setAlarm(time)
Schedule alarm to fire at specific time.
// Fire in 60 seconds
await this.ctx.storage.setAlarm(Date.now() + 60000);
// Fire at specific date
await this.ctx.storage.setAlarm(new Date('2025-12-31T23:59:59Z'));
Parameters:
time(number | Date): Timestamp or Date to fire
Returns: Promise
getAlarm()
Get current alarm time (null if not set).
const alarmTime = await this.ctx.storage.getAlarm();
if (alarmTime) {
console.log(`Alarm scheduled for ${new Date(alarmTime).toISOString()}`);
}
Returns: Promise<number | null>
deleteAlarm()
Delete scheduled alarm.
await this.ctx.storage.deleteAlarm();
Returns: Promise
Storage Limits
| Backend | Max Storage | deleteAll() Atomic |
|---|---|---|
| SQLite | 1 GB | ✅ Yes |
| KV | 128 MB | ❌ No (may be partial) |
Best Practices
✅ Always use parameterized queries (SQL)
// ✅ CORRECT
this.sql.exec('SELECT * FROM users WHERE id = ?', userId);
// ❌ WRONG (SQL injection risk)
this.sql.exec(`SELECT * FROM users WHERE id = ${userId}`);
✅ Use transactions for multi-step operations
this.ctx.storage.transactionSync(() => {
this.sql.exec('INSERT ...');
this.sql.exec('UPDATE ...');
});
✅ Create indexes for frequently queried columns
this.sql.exec('CREATE INDEX idx_user_email ON users(email)');
✅ Monitor storage size (approach 1GB limit)
const size = await this.estimateStorageSize();
if (size > 900_000_000) { // 900MB
await this.cleanup();
}
Official Docs: