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

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 retrieve
  • keys (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 store
  • value (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 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).

// 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: