Files
gh-jezweb-claude-skills-ski…/references/state-api-reference.md
2025-11-30 08:24:13 +08:00

294 lines
6.0 KiB
Markdown

# 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.
```typescript
// 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
```typescript
// 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)
```typescript
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.
```typescript
// 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 retrieve
- `keys` (string[]): Array of keys to retrieve
**Returns:** Promise<value> or Promise<Map<string, value>>
### `put(key, value)` / `put(entries)`
Put single or multiple values.
```typescript
// 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 store
- `value` (any): Value to store (must be serializable)
- `entries` (Record<string, any>): Object with key-value pairs
**Returns:** Promise<void>
### `delete(key)` / `delete(keys[])`
Delete single or multiple keys.
```typescript
// Delete single key
await this.ctx.storage.delete('key1');
// Delete multiple keys
await this.ctx.storage.delete(['key1', 'key2', 'key3']);
```
**Returns:** Promise<boolean> (true if deleted)
### `list(options)`
List keys with optional filtering.
```typescript
// 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 prefix
- `limit` (number): Max keys to return
- `reverse` (boolean): Reverse order
- `start` (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).
```typescript
// 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<void>
### `transaction(callback)`
Async transaction for KV operations.
```typescript
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<any> (callback return value)
---
## Alarms API
### `setAlarm(time)`
Schedule alarm to fire at specific time.
```typescript
// 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<void>
### `getAlarm()`
Get current alarm time (null if not set).
```typescript
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.
```typescript
await this.ctx.storage.deleteAlarm();
```
**Returns:** Promise<void>
---
## 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)
```typescript
// ✅ 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**
```typescript
this.ctx.storage.transactionSync(() => {
this.sql.exec('INSERT ...');
this.sql.exec('UPDATE ...');
});
```
**Create indexes for frequently queried columns**
```typescript
this.sql.exec('CREATE INDEX idx_user_email ON users(email)');
```
**Monitor storage size** (approach 1GB limit)
```typescript
const size = await this.estimateStorageSize();
if (size > 900_000_000) { // 900MB
await this.cleanup();
}
```
---
**Official Docs**:
- SQL API: https://developers.cloudflare.com/durable-objects/api/sqlite-storage-api/
- KV API: https://developers.cloudflare.com/durable-objects/api/legacy-kv-storage-api/