### Development Server Start Command Source: https://developers.cloudflare.com/durable-objects/tutorials/build-a-seat-booking-app Command to start the development server. Note that the frontend will not function correctly without a backend implementation. ```bash npm run dev ``` -------------------------------- ### Install Vitest and Workers Vitest Integration (yarn) Source: https://developers.cloudflare.com/durable-objects/examples/testing-with-durable-objects Install Vitest and the Workers Vitest integration using yarn. ```bash yarn add -D vitest@^4.1.0 @cloudflare/vitest-pool-workers ``` -------------------------------- ### Start Local Development Server Source: https://developers.cloudflare.com/durable-objects/tutorials/build-a-seat-booking-app Run this command to start a local development server for testing the seat booking application. The application will be accessible at http://localhost:8787. ```bash npm run dev ``` -------------------------------- ### Install Vitest and Workers Vitest Integration (pnpm) Source: https://developers.cloudflare.com/durable-objects/examples/testing-with-durable-objects Install Vitest and the Workers Vitest integration using pnpm. ```bash pnpm add -D vitest@^4.1.0 @cloudflare/vitest-pool-workers ``` -------------------------------- ### Install Vitest and Workers Vitest Integration (npm) Source: https://developers.cloudflare.com/durable-objects/examples/testing-with-durable-objects Install Vitest and the Workers Vitest integration using npm. ```bash npm i -D vitest@^4.1.0 @cloudflare/vitest-pool-workers ``` -------------------------------- ### Create Migration Example (TOML) Source: https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations/index.md Example wrangler.toml configuration for creating a new Durable Object binding named DURABLE_OBJECT_A with class DurableObjectAClass. ```toml [[durable_objects.bindings]] name = "DURABLE_OBJECT_A" class_name = "DurableObjectAClass" [[migrations]] tag = "v1" new_sqlite_classes = [ "DurableObjectAClass" ] ``` -------------------------------- ### Initialize Durable Object with Container Source: https://developers.cloudflare.com/durable-objects/llms-full.txt Boots the container when the Durable Object starts. This example shows initialization in both JavaScript and TypeScript. ```javascript export class MyDurableObject extends DurableObject { constructor(ctx, env) { super(ctx, env); this.ctx.blockConcurrencyWhile(async () => { this.ctx.container.start(); }); } } ``` ```typescript export class MyDurableObject extends DurableObject { constructor(ctx: DurableObjectState, env: Env) { super(ctx, env); this.ctx.blockConcurrencyWhile(async () => { this.ctx.container.start(); }); } } ``` -------------------------------- ### Install Vitest and Workers Vitest Integration (pnpm) Source: https://developers.cloudflare.com/durable-objects/examples/testing-with-durable-objects/index.md Install Vitest and the Workers Vitest integration as dev dependencies using pnpm. ```bash pnpm add -D vitest@^4.1.0 @cloudflare/vitest-pool-workers ``` -------------------------------- ### Transfer Migration Example Configuration Source: https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations An example of Wrangler configuration for transferring Durable Objects from 'DurableObjectExample' in 'OldWorkerScript' to 'TransferredClass' in the current Worker. ```json { // destination worker "durable_objects": { "bindings": [ { "name": "MY_DURABLE_OBJECT", "class_name": "TransferredClass" } ] }, // Transferring class "migrations": [ { "tag": "v4", "transferred_classes": [ { "from": "DurableObjectExample", "from_script": "OldWorkerScript", "to": "TransferredClass" } ] } ] } ``` ```toml [[durable_objects.bindings]] name = "MY_DURABLE_OBJECT" class_name = "TransferredClass" [[migrations]] tag = "v4" [[migrations.transferred_classes]] from = "DurableObjectExample" from_script = "OldWorkerScript" to = "TransferredClass" ``` -------------------------------- ### Install Vitest and Workers Vitest Integration (yarn) Source: https://developers.cloudflare.com/durable-objects/examples/testing-with-durable-objects/index.md Install Vitest and the Workers Vitest integration as dev dependencies using yarn. ```bash yarn add -D vitest@^4.1.0 @cloudflare/vitest-pool-workers ``` -------------------------------- ### Create Migration Example (JSONC) Source: https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations/index.md Example wrangler.jsonc configuration for creating a new Durable Object binding named DURABLE_OBJECT_A with class DurableObjectAClass. ```json { // Creating a new Durable Object class "durable_objects": { "bindings": [ { "name": "DURABLE_OBJECT_A", "class_name": "DurableObjectAClass" } ] }, // Add the lines below for a Create migration. "migrations": [ { "tag": "v1", "new_sqlite_classes": [ "DurableObjectAClass" ] } ] } ``` -------------------------------- ### Example Wrangler Configuration for Migrations Source: https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations Demonstrates how to define top-level default migrations and environment-specific migration overrides in a Wrangler configuration file. ```json { // top-level default migrations "migrations": [{ ... }], "env": { "staging": { // migration override for staging "migrations": [{...}] } } } ``` -------------------------------- ### Transfer Migration Example (JSONC) Source: https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations/index.md An example of a wrangler.jsonc file demonstrating a Transfer migration. It shows how to transfer Durable Objects from 'DurableObjectExample' in 'OldWorkerScript' to 'TransferredClass' in the current Worker. ```json { // destination worker "durable_objects": { "bindings": [ { "name": "MY_DURABLE_OBJECT", "class_name": "TransferredClass" } ] }, // Transferring class "migrations": [ { "tag": "v4", "transferred_classes": [ { "from": "DurableObjectExample", "from_script": "OldWorkerScript", "to": "TransferredClass" } ] } ] } ``` -------------------------------- ### Install Vitest and Workers Vitest Integration (npm) Source: https://developers.cloudflare.com/durable-objects/examples/testing-with-durable-objects/index.md Install Vitest and the Workers Vitest integration as dev dependencies using npm. ```bash npm i -D vitest@^4.1.0 @cloudflare/vitest-pool-workers ``` -------------------------------- ### Example Create Migration for Durable Object Binding Source: https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations This example shows a complete Wrangler configuration for creating a new Durable Object binding named DURABLE_OBJECT_A. It includes the Durable Object binding definition and the Create migration configuration. ```json { "durable_objects": { "bindings": [ { "name": "DURABLE_OBJECT_A", "class_name": "DurableObjectAClass" } ] }, "migrations": [ { "tag": "v1", "new_sqlite_classes": [ "DurableObjectAClass" ] } ] } ``` -------------------------------- ### Start Container with Options Source: https://developers.cloudflare.com/durable-objects/api/container/index.md Boots a container, optionally configuring environment variables, the entrypoint command, and internet access. This method does not wait for the container to be fully ready. ```javascript this.ctx.container.start({ env: { FOO: "bar", }, enableInternet: false, entrypoint: ["node", "server.js"], }); ``` -------------------------------- ### Navigate to the project directory Source: https://developers.cloudflare.com/durable-objects/tutorials/build-a-seat-booking-app Change into the newly created project directory to begin development. ```bash cd seat-booking ``` -------------------------------- ### Instantiate a Durable Object Stub Source: https://developers.cloudflare.com/durable-objects/reference/glossary An example of how to get a stub for a unique Durable Object within a namespace, which allows calling into that Durable Object. ```javascript let stub = env.MY_DURABLE_OBJECT.get(id) ``` -------------------------------- ### Example Durable Object (JavaScript) Source: https://developers.cloudflare.com/durable-objects/examples/testing-with-durable-objects/index.md A simple counter Durable Object with SQLite storage implemented in JavaScript. It includes methods for incrementing, getting, and resetting the count. ```javascript import { DurableObject } from "cloudflare:workers"; export class Counter extends DurableObject { constructor(ctx, env) { super(ctx, env); ctx.blockConcurrencyWhile(async () => { this.ctx.storage.sql.exec(` CREATE TABLE IF NOT EXISTS counters ( name TEXT PRIMARY KEY, value INTEGER NOT NULL DEFAULT 0 ) `); }); } async increment(name = "default") { this.ctx.storage.sql.exec( `INSERT INTO counters (name, value) VALUES (?, 1)` `ON CONFLICT(name) DO UPDATE SET value = value + 1`, name, ); const result = this.ctx.storage.sql .exec("SELECT value FROM counters WHERE name = ?", name) .one(); return result.value; } async getCount(name = "default") { const result = this.ctx.storage.sql .exec("SELECT value FROM counters WHERE name = ?", name) .toArray(); return result[0]?.value ?? 0; } async reset(name = "default") { this.ctx.storage.sql.exec("DELETE FROM counters WHERE name = ?", name); } } export default { async fetch(request, env) { const url = new URL(request.url); const counterId = url.searchParams.get("id") ?? "default"; const id = env.COUNTER.idFromName(counterId); const stub = env.COUNTER.get(id); if (request.method === "POST") { const count = await stub.increment(); return Response.json({ count }); } const count = await stub.getCount(); return Response.json({ count }); }, }; ``` -------------------------------- ### Example Durable Object (TypeScript) Source: https://developers.cloudflare.com/durable-objects/examples/testing-with-durable-objects/index.md A simple counter Durable Object with SQLite storage implemented in TypeScript. It includes methods for incrementing, getting, and resetting the count, with type safety. ```typescript import { DurableObject } from "cloudflare:workers"; export interface Env { COUNTER: DurableObjectNamespace; } export class Counter extends DurableObject { constructor(ctx: DurableObjectState, env: Env) { super(ctx, env); ctx.blockConcurrencyWhile(async () => { this.ctx.storage.sql.exec(` CREATE TABLE IF NOT EXISTS counters ( name TEXT PRIMARY KEY, value INTEGER NOT NULL DEFAULT 0 ) `); }); } async increment(name: string = "default"): Promise { this.ctx.storage.sql.exec( `INSERT INTO counters (name, value) VALUES (?, 1)` `ON CONFLICT(name) DO UPDATE SET value = value + 1`, name ); const result = this.ctx.storage.sql .exec<{ value: number }>("SELECT value FROM counters WHERE name = ?", name) .one(); return result.value; } async getCount(name: string = "default"): Promise { const result = this.ctx.storage.sql .exec<{ value: number }>("SELECT value FROM counters WHERE name = ?", name) .toArray(); return result[0]?.value ?? 0; } async reset(name: string = "default"): Promise { this.ctx.storage.sql.exec("DELETE FROM counters WHERE name = ?", name); } } export default { async fetch(request: Request, env: Env): Promise { const url = new URL(request.url); const counterId = url.searchParams.get("id") ?? "default"; const id = env.COUNTER.idFromName(counterId); const stub = env.COUNTER.get(id); if (request.method === "POST") { const count = await stub.increment(); return Response.json({ count }); } const count = await stub.getCount(); return Response.json({ count }); }, }; ``` -------------------------------- ### Create Cloudflare Agent Starter Project Source: https://developers.cloudflare.com/durable-objects/llms-full.txt Initializes a new Cloudflare agent project using the create-cloudflare CLI. ```bash npx create-cloudflare@latest --template cloudflare/agents-starter cd agents-starter && npm install npm run dev ``` -------------------------------- ### Example Counter Durable Object (JavaScript) Source: https://developers.cloudflare.com/durable-objects/examples/testing-with-durable-objects A simple counter Durable Object implemented in JavaScript, utilizing SQLite for storage. It includes methods for incrementing, getting counts, and resetting the counter. ```javascript import { DurableObject } from "cloudflare:workers"; export class Counter extends DurableObject { constructor(ctx, env) { super(ctx, env); ctx.blockConcurrencyWhile(async () => { this.ctx.storage.sql.exec $( ` CREATE TABLE IF NOT EXISTS counters ( name TEXT PRIMARY KEY, value INTEGER NOT NULL DEFAULT 0 ) ` ); }); } async increment(name = "default") { this.ctx.storage.sql.exec( `INSERT INTO counters (name, value) VALUES (?, 1) ON CONFLICT(name) DO UPDATE SET value = value + 1`, name, ); const result = this.ctx.storage.sql .exec("SELECT value FROM counters WHERE name = ?", name) .one(); return result.value; } async getCount(name = "default") { const result = this.ctx.storage.sql .exec("SELECT value FROM counters WHERE name = ?", name) .toArray(); return result[0]?.value ?? 0; } async reset(name = "default") { this.ctx.storage.sql.exec("DELETE FROM counters WHERE name = ?", name); } } export default { async fetch(request, env) { const url = new URL(request.url); const counterId = url.searchParams.get("id") ?? "default"; const id = env.COUNTER.idFromName(counterId); const stub = env.COUNTER.get(id); if (request.method === "POST") { const count = await stub.increment(); return Response.json({ count }); } const count = await stub.getCount(); return Response.json({ count }); }, }; ``` -------------------------------- ### container.start Source: https://developers.cloudflare.com/durable-objects/llms-full.txt Boots a container. This method does not block until the container is fully started. You may want to confirm the container is ready to accept requests before using it. ```APIDOC ## Methods ### `start` `start` boots a container. This method does not block until the container is fully started. You may want to confirm the container is ready to accept requests before using it. ```javascript this.ctx.container.start({ env: { FOO: "bar", }, enableInternet: false, entrypoint: ["node", "server.js"], }); ``` #### Parameters * `options` (optional): An object with the following properties: * `env`: An object containing environment variables to pass to the container. This is useful for passing configuration values or secrets to the container. * `entrypoint`: An array of strings representing the command to run in the container. * `enableInternet`: A boolean indicating whether to enable internet access for the container. #### Return values * None. ``` -------------------------------- ### Iterating Raw Rows and Consuming Cursor Source: https://developers.cloudflare.com/durable-objects/llms-full.txt Demonstrates how to use the raw() iterator to get rows as arrays and then consume the rest of the cursor using toArray(). This example shows both TypeScript and Python implementations. ```typescript let cursor = this.sql.exec("SELECT * FROM artist ORDER BY artistname ASC;"); let rawResult = cursor.raw().next(); if (!rawResult.done) { console.log(rawResult.value); // prints [ 123, 'Alice' ] } else { // query returned zero results } console.log(cursor.toArray()); // prints [{ artistid: 456, artistname: 'Bob' },{ artistid: 789, artistname: 'Charlie' }] ``` ```python cursor = self.sql.exec("SELECT * FROM artist ORDER BY artistname ASC;") raw_result = cursor.raw().next() if not raw_result.done: print(raw_result.value) # prints [ 123, 'Alice' ] else: # query returned zero results pass print(cursor.toArray()) # prints [{ artistid: 456, artistname: 'Bob' },{ artistid: 789, artistname: 'Charlie' }] ``` -------------------------------- ### Wrangler Configuration for Durable Object Alarms Source: https://developers.cloudflare.com/durable-objects/examples/alarms-api Configures a Wrangler project to use a Durable Object named 'Batcher' with specific bindings and migrations. This setup is essential for deploying the Alarms API example. ```json { "$schema": "./node_modules/wrangler/config-schema.json", "name": "durable-object-alarm", "main": "src/index.ts", "durable_objects": { "bindings": [ { "name": "BATCHER", "class_name": "Batcher" } ] }, "migrations": [ { "tag": "v1", "new_sqlite_classes": [ "Batcher" ] } ] } ``` -------------------------------- ### Deploy the Application Source: https://developers.cloudflare.com/durable-objects/tutorials/build-a-seat-booking-app/index.md Run this command to deploy the application to Cloudflare Workers. ```bash npm run deploy ``` -------------------------------- ### Accessing Durable Object Name via Stub (JavaScript) Source: https://developers.cloudflare.com/durable-objects/api/stub This example shows how to get a stub for a named Durable Object and asserts that the stub's name property correctly reflects the provided name. ```javascript const stub = env.MY_DURABLE_OBJECT.getByName("foo"); console.assert(stub.name === "foo", "This should always be true"); ``` -------------------------------- ### JavaScript Integration Tests for Durable Objects Source: https://developers.cloudflare.com/durable-objects/examples/testing-with-durable-objects/index.md Use `exports.default.fetch()` to simulate HTTP requests to your Worker and test Durable Object interactions. This example shows how to test incrementing a counter via POST and retrieving its value via GET, and verifies that different Durable Objects are isolated by ID. ```javascript import { exports } from "cloudflare:workers"; import { describe, it, expect } from "vitest"; describe("Counter Worker integration", () => { it("should increment via HTTP POST", async () => { const response = await exports.default.fetch( "http://example.com?id=http-test", { method: "POST", }, ); expect(response.status).toBe(200); const data = await response.json(); expect(data.count).toBe(1); }); it("should get count via HTTP GET", async () => { // First increment the counter await exports.default.fetch("http://example.com?id=get-test", { method: "POST", }); await exports.default.fetch("http://example.com?id=get-test", { method: "POST", }); // Then get the count const response = await exports.default.fetch( "http://example.com?id=get-test", ); const data = await response.json(); expect(data.count).toBe(2); }); it("should use different counters for different IDs", async () => { await exports.default.fetch("http://example.com?id=counter-a", { method: "POST", }); await exports.default.fetch("http://example.com?id=counter-a", { method: "POST", }); await exports.default.fetch("http://example.com?id=counter-b", { method: "POST", }); const responseA = await exports.default.fetch( "http://example.com?id=counter-a", ); const responseB = await exports.default.fetch( "http://example.com?id=counter-b", ); const dataA = await responseA.json(); const dataB = await responseB.json(); expect(dataA.count).toBe(2); expect(dataB.count).toBe(1); }); }); ``` -------------------------------- ### WebSocketHibernationServer Example Source: https://developers.cloudflare.com/durable-objects/examples/websocket-hibernation-server This example demonstrates a WebSocketHibernationServer, a class that can be used for managing WebSocket connections. ```python new_sqlite_classes = [ "WebSocketHibernationServer" ] ``` -------------------------------- ### Example Create Migration for Durable Object Binding (TOML) Source: https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations This TOML configuration demonstrates how to set up a new Durable Object binding 'DURABLE_OBJECT_A' and its corresponding Create migration. The 'tag' identifies the migration, and 'new_sqlite_classes' lists the new class. ```toml [[durable_objects.bindings]] name = "DURABLE_OBJECT_A" class_name = "DurableObjectAClass" [[migrations]] tag = "v1" new_sqlite_classes = [ "DurableObjectAClass" ] ``` -------------------------------- ### Initialize Storage and Run Migrations in Constructor (JavaScript) Source: https://developers.cloudflare.com/durable-objects/best-practices/rules-of-durable-objects Use `blockConcurrencyWhile()` in the constructor to run migrations and initialize state before any requests are processed. This ensures your schema is ready and prevents race conditions during initialization. This example demonstrates manual schema version tracking. ```javascript import { DurableObject } from "cloudflare:workers"; export class ChatRoom extends DurableObject { constructor(ctx, env) { super(ctx, env); // blockConcurrencyWhile() ensures no requests are processed until this completes ctx.blockConcurrencyWhile(async () => { await this.migrate(); }); } async migrate() { // Create the migrations tracking table if it does not exist this.ctx.storage.sql.exec ( ` CREATE TABLE IF NOT EXISTS _sql_schema_migrations ( id INTEGER PRIMARY KEY, applied_at TEXT NOT NULL DEFAULT (datetime('now')) ); ` ); // Determine the current schema version const version = this.ctx.storage.sql .exec( "SELECT COALESCE(MAX(id), 0) as version FROM _sql_schema_migrations", ) .one().version; if (version < 1) { this.ctx.storage.sql.exec(` CREATE TABLE IF NOT EXISTS messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT NOT NULL, content TEXT NOT NULL, created_at INTEGER NOT NULL ); CREATE INDEX IF NOT EXISTS idx_messages_created_at ON messages(created_at); INSERT INTO _sql_schema_migrations (id) VALUES (1); `); } if (version < 2) { // Future migration: add a new column this.ctx.storage.sql.exec(` ALTER TABLE messages ADD COLUMN edited_at INTEGER; INSERT INTO _sql_schema_migrations (id) VALUES (2); `); } } } ``` -------------------------------- ### Create and Populate Artist Table Source: https://developers.cloudflare.com/durable-objects/llms-full.txt Initializes the SQL storage by creating an 'artist' table and inserting sample data. This is typically done in the constructor of a Durable Object. ```TypeScript import { DurableObject } from "cloudflare:workers"; export class MyDurableObject extends DurableObject { sql: SqlStorage constructor(ctx: DurableObjectState, env: Env) { super(ctx, env); this.sql = ctx.storage.sql; this.sql.exec(`CREATE TABLE IF NOT EXISTS artist( artistid INTEGER PRIMARY KEY, artistname TEXT );INSERT INTO artist (artistid, artistname) VALUES (123, 'Alice'), (456, 'Bob'), (789, 'Charlie');` ); } } ``` -------------------------------- ### Create SQL Indexes for Durable Objects (JavaScript) Source: https://developers.cloudflare.com/durable-objects/best-practices/rules-of-durable-objects Demonstrates how to create SQL tables and indexes within a Durable Object constructor using JavaScript. Indexes improve read performance for frequently filtered columns. ```javascript import { DurableObject } from "cloudflare:workers"; export class ChatRoom extends DurableObject { constructor(ctx, env) { super(ctx, env); ctx.blockConcurrencyWhile(async () => { this.ctx.storage.sql.exec(` CREATE TABLE IF NOT EXISTS messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT NOT NULL, content TEXT NOT NULL, created_at INTEGER NOT NULL ); -- Index for queries filtering by user CREATE INDEX IF NOT EXISTS idx_messages_user_id ON messages(user_id); -- Index for time-based queries (recent messages) CREATE INDEX IF NOT EXISTS idx_messages_created_at ON messages(created_at); -- Composite index for user + time queries CREATE INDEX IF NOT EXISTS idx_messages_user_time ON messages(user_id, created_at); `); }); } // This query benefits from idx_messages_user_time async getUserMessages(userId, since) { return this.ctx.storage.sql .exec( "SELECT * FROM messages WHERE user_id = ? AND created_at > ? ORDER BY created_at", userId, since, ) .toArray(); } } ``` -------------------------------- ### Navigate to the new Worker project directory Source: https://developers.cloudflare.com/durable-objects/get-started/index.md After creating the Worker project, change into the newly created directory to begin configuring and developing your application. ```bash cd durable-object-starter ``` -------------------------------- ### Set and Handle Alarms in JavaScript Source: https://developers.cloudflare.com/durable-objects/api/alarms Demonstrates how to set an alarm for 10 seconds in the future and handle it using the `alarm()` method in JavaScript. This snippet shows basic setup for a Durable Object that interacts with storage. ```javascript import { DurableObject } from "cloudflare:workers"; export default { async fetch(request, env) { return await env.ALARM_EXAMPLE.getByName("foo").fetch(request); }, }; const SECONDS = 1000; export class AlarmExample extends DurableObject { constructor(ctx, env) { super(ctx, env); this.storage = ctx.storage; } async fetch(request) { // If there is no alarm currently set, set one for 10 seconds from now let currentAlarm = await this.storage.getAlarm(); if (currentAlarm == null) { this.storage.setAlarm(Date.now() + 10 * SECONDS); } } async alarm() { // The alarm handler will be invoked whenever an alarm fires. // You can use this to do work, read from the Storage API, make HTTP calls // and set future alarms to run using this.storage.setAlarm() from within this handler. } } ``` -------------------------------- ### Delete Migration Example Configuration Source: https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations Example of a Delete migration configuration to remove the `DeprecatedObjectClass` Durable Object. Remember to remove the binding from your Worker. ```json { "migrations": [ { "tag": "v3", "deleted_classes": [ "DeprecatedObjectClass" ] } ] } ``` ```toml [[migrations]] tag = "v3" deleted_classes = [ "DeprecatedObjectClass" ] ``` -------------------------------- ### Durable Object LIST with start option Source: https://developers.cloudflare.com/durable-objects/llms-full.txt Specify the `start` option to begin listing keys from a specific key (inclusive) in ascending order. ```javascript await this.storage.list({ start: "key1" }); ``` -------------------------------- ### Rename Migration Example Configuration Source: https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations Example of a Rename migration to change a Durable Object class from `OldName` to `UpdatedName`. Ensure the binding in your Wrangler config is updated. ```json { "durable_objects": { "bindings": [ { "name": "MY_DURABLE_OBJECT", "class_name": "UpdatedName" } ] }, "migrations": [ { "tag": "v3", "renamed_classes": [ { "from": "OldName", "to": "UpdatedName" } ] } ] } ``` ```toml [[durable_objects.bindings]] name = "MY_DURABLE_OBJECT" class_name = "UpdatedName" [[migrations]] tag = "v3" [[migrations.renamed_classes]] from = "OldName" to = "UpdatedName" ``` -------------------------------- ### RPC Session Billing Example Source: https://developers.cloudflare.com/durable-objects/platform/pricing Demonstrates how RPC method calls are billed. Initial stub retrieval and method calls are billed as requests, while subsequent calls on a returned stub are part of the same RPC session and not billed separately. ```javascript let durableObjectStub = OBJECT_NAMESPACE.get(id); // retrieve Durable Object stub using foo = await durableObjectStub.bar(); // billed as a request await foo.baz(); // treated as part of the same RPC session created by calling bar(), not billed as a request await durableObjectStub.cat(); // billed as a request ``` -------------------------------- ### Using `startAfter` Option for Durable Object List Source: https://developers.cloudflare.com/durable-objects/api/sqlite-storage-api/index.md Retrieve keys starting exclusively after a specified key using the `startAfter` option. This option cannot be used with `start`. ```javascript const dataAfterKey = await myDurableObject.storage.list({ startAfter: "key1" }); ``` -------------------------------- ### Get Durable Object by deterministic name (TypeScript) Source: https://developers.cloudflare.com/durable-objects/best-practices/rules-of-durable-objects Use `env.NAMESPACE.getByName(name)` to get a stub for a Durable Object. All requests for the same name will consistently route to the same instance. ```typescript import { DurableObject } from "cloudflare:workers"; export interface Env { GAME_SESSION: DurableObjectNamespace; } export class GameSession extends DurableObject { async join(playerId: string) { // Game logic here } } export default { async fetch(request: Request, env: Env): Promise { const url = new URL(request.url); const gameId = url.searchParams.get("game"); if (!gameId) { return new Response("Missing game ID", { status: 400 }); } // ✅ Good: Deterministic ID from a meaningful string // All requests for "game-abc123" go to the same Durable Object const stub = env.GAME_SESSION.getByName(gameId); await stub.join("player-xyz"); return new Response("Joined game"); }, }; ``` -------------------------------- ### SQL API Usage Source: https://developers.cloudflare.com/durable-objects/api/sqlite-storage-api/index.md Examples demonstrating how to execute SQL queries and process results using the `sql.exec()` method. This includes iterating over rows, converting results to arrays, and accessing specific rows. ```APIDOC ## SQL API Usage Examples This section provides examples for interacting with the SQLite database within Durable Objects. ### Executing SQL and Iterating Results ```typescript let cursor = this.sql.exec("SELECT * FROM artist;"); for (let row of cursor) { // Iterate over row object and do something } ``` ### Converting Results to Array of Row Objects ```typescript // Returns array of row objects: [{"artistid":123,"artistname":"Alice"},{"artistid":456,"artistname":"Bob"},{"artistid":789,"artistname":"Charlie"}] let resultsArray1 = this.sql.exec("SELECT * FROM artist;").toArray(); // OR let resultsArray2 = Array.from(this.sql.exec("SELECT * FROM artist;")); // OR let resultsArray3 = [...this.sql.exec("SELECT * FROM artist;")]; // JavaScript spread syntax ``` ### Converting Results to Array of Row Values Arrays ```typescript // Returns [[123,"Alice"],[456,"Bob"],[789,"Charlie"]] let cursor = this.sql.exec("SELECT * FROM artist;"); let resultsArray = cursor.raw().toArray(); // Returns ["artistid","artistname"] let columnNameArray = this.sql.exec("SELECT * FROM artist;").columnNames.toArray(); ``` ### Getting the First Row Object ```typescript // Returns {"artistid":123,"artistname":"Alice"} let firstRow = this.sql.exec("SELECT * FROM artist ORDER BY artistname DESC;").toArray()[0]; ``` ### Checking for Exactly One Row ```typescript // returns error this.sql.exec("SELECT * FROM artist ORDER BY artistname ASC;").one(); // returns { artistid: 123, artistname: 'Alice' } let oneRow = this.sql.exec("SELECT * FROM artist WHERE artistname = ?;", "Alice").one() ``` ### Cursor Behavior ```typescript let cursor = this.sql.exec("SELECT * FROM artist ORDER BY artistname ASC;"); let result = cursor.next(); if (!result.done) { console.log(result.value); // prints { artistid: 123, artistname: 'Alice' } } else { // query returned zero results } let remainingRows = cursor.toArray(); console.log(remainingRows); // prints [{ artistid: 456, artistname: 'Bob' },{ artistid: 789, artistname: 'Charlie' }] ``` ### Raw Cursor Behavior ```typescript let cursor = this.sql.exec("SELECT * FROM artist ORDER BY artistname ASC;"); let result = cursor.raw().next(); if (!result.done) { console.log(result.value); // prints [ 123, 'Alice' ] } else { // query returned zero results } console.log(cursor.toArray()); // prints [{ artistid: 456, artistname: 'Bob' },{ artistid: 789, artistname: 'Charlie' }] ``` ### `rowsRead()` Method ```typescript let cursor = this.sql.exec("SELECT * FROM artist;"); cursor.next() console.log(cursor.rowsRead); // prints 1 cursor.toArray(); // consumes remaining cursor console.log(cursor.rowsRead); // prints 3 ``` ``` -------------------------------- ### Durable Object and Worker Setup (Python) Source: https://developers.cloudflare.com/durable-objects/api/namespace Sets up a Durable Object class and a basic Worker entrypoint in Python that obtains a stub to a named Durable Object. ```python from workers import DurableObject, WorkerEntrypoint # Durable Object class MyDurableObject(DurableObject): pass # Worker class Default(WorkerEntrypoint): async def fetch(self, request): # A stub is a client Object used to invoke methods defined by the Durable Object stub = self.env.MY_DURABLE_OBJECT.getByName("foo") # ... ``` -------------------------------- ### Get Durable Object by deterministic name (JavaScript) Source: https://developers.cloudflare.com/durable-objects/best-practices/rules-of-durable-objects Use `env.NAMESPACE.getByName(name)` to get a stub for a Durable Object. All requests for the same name will consistently route to the same instance. ```javascript import { DurableObject } from "cloudflare:workers"; export class GameSession extends DurableObject { async join(playerId) { // Game logic here } } export default { async fetch(request, env) { const url = new URL(request.url); const gameId = url.searchParams.get("game"); if (!gameId) { return new Response("Missing game ID", { status: 400 }); } // ✅ Good: Deterministic ID from a meaningful string // All requests for "game-abc123" go to the same Durable Object const stub = env.GAME_SESSION.getByName(gameId); await stub.join("player-xyz"); return new Response("Joined game"); }, }; ``` -------------------------------- ### Example Create Migration with Durable Object Binding (TOML) Source: https://developers.cloudflare.com/durable-objects/llms-full.txt This TOML configuration demonstrates how to set up a new Durable Object binding named DURABLE_OBJECT_A and apply a 'Create' migration for its class 'DurableObjectAClass'. This is used when introducing a new Durable Object to your Worker. ```toml [[durable_objects.bindings]] name = "DURABLE_OBJECT_A" class_name = "DurableObjectAClass" [[migrations]] tag = "v1" new_sqlite_classes = [ "DurableObjectAClass" ] ``` -------------------------------- ### Durable Object LIST with start and end options Source: https://developers.cloudflare.com/durable-objects/api/legacy-kv-storage-api/index.md Paginate through keys and values using `start` (inclusive) and `end` (exclusive) options to define a specific range of keys. ```javascript const listResult = await myDurableObject.storage.list({ start: "key1", end: "key5" }); ``` -------------------------------- ### Create SQL Indexes for Durable Objects (TypeScript) Source: https://developers.cloudflare.com/durable-objects/best-practices/rules-of-durable-objects Demonstrates how to create SQL tables and indexes within a Durable Object constructor using TypeScript. Indexes improve read performance for frequently filtered columns. ```typescript import { DurableObject } from "cloudflare:workers"; export interface Env { CHAT_ROOM: DurableObjectNamespace; } export class ChatRoom extends DurableObject { constructor(ctx: DurableObjectState, env: Env) { super(ctx, env); ctx.blockConcurrencyWhile(async () => { this.ctx.storage.sql.exec(` CREATE TABLE IF NOT EXISTS messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT NOT NULL, content TEXT NOT NULL, created_at INTEGER NOT NULL ); -- Index for queries filtering by user CREATE INDEX IF NOT EXISTS idx_messages_user_id ON messages(user_id); -- Index for time-based queries (recent messages) CREATE INDEX IF NOT EXISTS idx_messages_created_at ON messages(created_at); -- Composite index for user + time queries CREATE INDEX IF NOT EXISTS idx_messages_user_time ON messages(user_id, created_at); `); }); } // This query benefits from idx_messages_user_time async getUserMessages(userId: string, since: number) { return this.ctx.storage.sql .exec( "SELECT * FROM messages WHERE user_id = ? AND created_at > ? ORDER BY created_at", userId, since ) .toArray(); } } ``` -------------------------------- ### Durable Object and Worker Example Source: https://developers.cloudflare.com/durable-objects/api/stub/index.md Demonstrates how to define a Durable Object with a method and how a Worker can invoke that method using a stub. This example shows the basic structure for interacting with Durable Objects. ```javascript import { DurableObject } from "cloudflare:workers"; // Durable Object export class MyDurableObject extends DurableObject { constructor(ctx, env) { super(ctx, env); } async sayHello() { return "Hello, World!"; } } // Worker export default { async fetch(request, env) { // A stub is a client used to invoke methods on the Durable Object const stub = env.MY_DURABLE_OBJECT.getByName("foo"); // Methods on the Durable Object are invoked via the stub const rpcResponse = await stub.sayHello(); return new Response(rpcResponse); }, }; ``` ```typescript import { DurableObject } from "cloudflare:workers"; export interface Env { MY_DURABLE_OBJECT: DurableObjectNamespace; } // Durable Object export class MyDurableObject extends DurableObject { constructor(ctx: DurableObjectState, env: Env) { super(ctx, env); } async sayHello(): Promise { return "Hello, World!"; } } // Worker export default { async fetch(request, env) { // A stub is a client used to invoke methods on the Durable Object const stub = env.MY_DURABLE_OBJECT.getByName("foo"); // Methods on the Durable Object are invoked via the stub const rpcResponse = await stub.sayHello(); return new Response(rpcResponse); }, } satisfies ExportedHandler; ```