Skip to content

SQLite-backed Durable Object Storage

Last updated View as MarkdownAgent setup

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.

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 or KV.

Methods 1 SQLite-backed Durable Object class KV-backed Durable Object class
SQL API ✅ ❌
PITR API ✅ ❌
Synchronous KV API ✅ 2, 3 ❌
Asynchronous KV API ✅ 3 ✅
Alarms API ✅ ✅

Footnotes

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 __cf_kv. Note that you will be able to view this table when listing all tables, but you will not be able to access its content through the SQL API.

3 SQLite-backed Durable Objects also use synchronous KV API methods using ctx.storage.kv, whereas KV-backed Durable Objects only provide asynchronous KV API methods.

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.

export class Counter extends DurableObject {
	constructor(ctx, env) {
		super(ctx, env);
	}

	async increment() {
		let value = (await this.ctx.storage.get("value")) || 0;
		value += 1;
		await this.ctx.storage.put("value", value);
		return value;
	}
}
export class Counter extends DurableObject {
  constructor(ctx: DurableObjectState, env: Env) {
    super(ctx, env);
  }

    async increment(): Promise<number> {
      let value: number = (await this.ctx.storage.get('value')) || 0;
      value += 1;
      await this.ctx.storage.put('value', value);
      return value;
    }

}
from workers import DurableObject

class Counter(DurableObject):
  def __init__(self, ctx, env):
    super().__init__(ctx, env)

  async def increment(self):
    value = (await self.ctx.storage.get('value')) or 0
    value += 1
    await self.ctx.storage.put('value', value)
    return value

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 ↗︎.

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 of DurableObjectStorage class.

For example, using sql.exec() a user can create a table and insert rows.

import { DurableObject } from "cloudflare:workers";

export class MyDurableObject extends DurableObject {
  sql: