このガイドでは、次の作業を順に進めます。
- Durable Object を定義する JavaScript クラスを書く。
- Durable Objects SQL API で、Durable Object 専用の組み込み SQLite データベースを照会する。
- 別の Worker から Durable Object をインスタンス化し、通信する。
- Durable Object と、その Durable Object と通信する Worker をデプロイする。
Durable Objects の詳細は What are Durable Objects? を参照してください。
手順を飛ばしてすぐに始めたい場合は、次のボタンを選びます。
GitHub アカウントにリポジトリが作成され、アプリケーションが Cloudflare Workers へデプロイされます。Cloudflare Workers に慣れていて、手順ごとの案内を飛ばしたい場合に使います。
Cloudflare Workers が初めてなら、手順を手作業で進める方がよいことがあります。
- Cloudflare アカウント ↗ に登録します。
Node.js↗ をインストールします。
Node.js のバージョンマネージャー
権限の問題を避け、Node.js のバージョンを切り替えられるよう、Volta ↗ や nvm ↗ などの Node バージョンマネージャーを使います。このガイドの後半で説明する Wrangler には、Node バージョン 16.17.0 以降が必要です。
Durable Object には Worker からアクセスします。Worker アプリケーションは、Durable Object とやり取りするためのインターフェイスです。
Worker プロジェクトを作成するには、次を実行します。
npm create cloudflare@latest -- durable-object-starteryarn create cloudflare durable-object-starterpnpm create cloudflare@latest durable-object-startercreate cloudflare@latest を実行すると、Workers CLI である Wrangler がインストールされます。プロジェクトのテストとデプロイには Wrangler を使います。
セットアップでは、次のオプションを選びます。
- What would you like to start with? では、
Hello World exampleを選びます。 - Which template would you like to use? では、
Worker + Durable Objectsを選びます。 - Which language do you want to use? では、
TypeScriptを選びます。 - Do you want to use git for version control? では、
Yesを選びます。 - Do you want to deploy your application? では、
Noを選びます(デプロイ前にいくつか変更します)。
新しいディレクトリが作成されます。コードを書く src/index.js または src/index.ts と、wrangler.jsonc 設定ファイルが含まれます。
新しいディレクトリへ移動します。
cd durable-object-starterDurable Object を作成してアクセスする前に、通常のエクスポートされた JavaScript クラスで振る舞いを定義する必要があります。
MyDurableObject クラスのコンストラクターには 2 つのパラメーターがあります。最初のパラメーター ctx には、その Durable Object 固有の状態(ストレージへアクセスするメソッドを含む)が入ります。2 番目のパラメーター env には、アップロード時に Worker へ関連付けたバインディングが入ります。
export class MyDurableObject extends DurableObject {
constructor(ctx, env) {
// Required, as we're extending the base class.
super(ctx, env);
}
}export class MyDurableObject extends DurableObject<Env> {
constructor(ctx: DurableObjectState, env: Env) {
// Required, as we're extending the base class.
super(ctx, env)
}
}from workers import DurableObject
class MyDurableObject(DurableObject):
def __init__(self, ctx, env):
super().__init__(ctx, env)Worker は リモートプロシージャコール(RPC) で Durable Object と通信します。Durable Object クラスのパブリックメソッドは、別の Worker から呼び出せる RPC メソッド として公開されます。
ファイルは次のようになります。
export class MyDurableObject extends DurableObject {
constructor(ctx, env) {
// Required, as we're extending the base class.
super(ctx, env);
}
async sayHello() {
let result = this.ctx.storage.sql
.exec("SELECT 'Hello, World!' as greeting")
.one();
return result.greeting;
}
}export class MyDurableObject extends DurableObject<Env> {
constructor(ctx: DurableObjectState, env: Env) {
// Required, as we're extending the base class.
super(ctx, env)
}
async sayHello(): Promise<string> {
let result = this.ctx.storage.sql
.exec("SELECT 'Hello, World!' as greeting")
.one();
return result.greeting;
}
}from workers import DurableObject
class MyDurableObject(DurableObject):
async def say_hello(self):
result = self.ctx.storage.sql.exec(
"SELECT 'Hello, World!' as greeting"
).one()
return result.greeting上記のコードでは、次を行っています。
- Worker から Durable Object と通信するために呼べる RPC メソッド
sayHello()を定義しています。 - SQL API のメソッド(
sql.exec())をctx.storage経由で使い、そのオブジェクトだけがアクセスできる専用の SQLite データベースである Durable Object のストレージにアクセスしています。 one()で、クエリ結果がちょうど 1 行であることを確認し、その 1 行を表すオブジェクトを返しています。- 行オブジェクトの
greeting列を返しています。
Durable Object へのアクセスには Worker を使います。Worker から Durable Object にアクセスする を参照してください。
Durable Object と通信するには、Worker の fetch ハンドラーを次のようにします。
export default {
async fetch(request, env, ctx) {
const stub = env.MY_DURABLE_OBJECT.getByName(new URL(request.url).pathname);
const greeting = await stub.sayHello();
return new Response(greeting);
},
};export default {
async fetch(request, env, ctx): Promise<Response> {
const stub = env.MY_DURABLE_OBJECT.getByName(new URL(request.url).pathname);
const greeting = await stub.sayHello();
return new Response(greeting);
},
} satisfies ExportedHandler<Env>;from workers import handler, Response, WorkerEntrypoint
from urllib.parse import urlparse
class Default(WorkerEntrypoint):
async def fetch(request):
url = urlparse(request.url)
stub = self.env.MY_DURABLE_OBJECT.getByName(url.path)
greeting = await stub.say_hello()
return Response(greeting)上記のコードでは、次を行っています。
- HTTP リクエストを受け取る
fetch()ハンドラーなど、Worker のメインイベントハンドラーをエクスポートしています。 fetch()ハンドラーにenvを渡しています。バインディングは、イベントハンドラーまたはクラスコンストラクターが呼ばれたときに 2 番目のパラメーターとして渡される環境オブジェクトのプロパティとして届きます。- 指定した名前に基づいて Durable Object インスタンスのスタブを構築しています。スタブは、Durable Object へメッセージを送るためのクライアントオブジェクトです。
- Durable Object の RPC メソッド
sayHello()を呼び出して Durable Object と通信し、Hello, World!という挨拶文字列を受け取っています。 return new Response()で HTTP Response を構築し、クライアントへ HTTP レスポンスを返しています。
Durable Object との通信の詳細は Worker から Durable Object にアクセスする を参照してください。
バインディング により、Worker は Cloudflare 開発者プラットフォーム上のリソースとやり取りできます。Worker プロジェクトの Wrangler 設定ファイル にある Durable Object バインディングには、バインディング名(このガイドでは MY_DURABLE_OBJECT)とクラス名(MyDurableObject)を含めます。
{
"durable_objects": {
"bindings": [
{
"name": "MY_DURABLE_OBJECT",
"class_name": "MyDurableObject"
}
]
}
}[[durable_objects.bindings]]
name = "MY_DURABLE_OBJECT"
class_name = "MyDurableObject"bindings セクションには次のフィールドがあります。
name- 必須。Worker 内で使うバインディング名です。class_name- 必須。バインドするクラス名です。script_name- 任意。デフォルトは、現在の 環境 の Worker コードです。
Worker がエクスポートする各 Durable Object クラスは、Wrangler 設定ファイルの exports フィールドで宣言します。Cloudflare はこの宣言を使い、初回デプロイ時にクラスの名前空間をプロビジョニングし、以降のデプロイではライフサイクル(名前変更、削除、転送)を管理します。
SQLite ストレージを持つ新しい Durable Object クラスを登録する最小の exports ブロックは次のとおりです。
{
"exports": {
"MyDurableObject": {
"type": "durable-object",
"storage": "sqlite"
}
}
}[exports.MyDurableObject]
type = "durable-object"
storage = "sqlite"Durable Object クラスの宣言と管理の詳細は Durable Object クラスのエクスポート を参照してください。レガシーの migrations 配列を使っている既存の Worker がある場合は Durable Object クラスのマイグレーション(レガシー) を参照してください。
Durable Object をローカルでテストするには、wrangler dev を実行します。
npx wrangler devコンソールに、Durable Object が返す Hello world 文字列が表示されます。
Durable Object Worker をデプロイするには、次を実行します。
npx wrangler deployデプロイ後、Cloudflare ダッシュボードで作成した Durable Object Worker を確認できます。
Workers & Pages を開く ↗Durable Object Worker は <YOUR_WORKER>.<YOUR_SUBDOMAIN>.workers.dev でプレビューできます。
最終的なコードは次のようになります。
import { DurableObject } from "cloudflare:workers";
export class MyDurableObject extends DurableObject {
constructor(ctx, env) {
// Required, as we are extending the base class.
super(ctx, env);
}
async sayHello() {
let result = this.ctx.storage.sql
.exec("SELECT 'Hello, World!' as greeting")
.one();
return result.greeting;
}
}
export default {
async fetch(request, env, ctx) {
const stub = env.MY_DURABLE_OBJECT.getByName(new URL(request.url).pathname);
const greeting = await stub.sayHello();
return new Response(greeting);
},
};import { DurableObject } from "cloudflare:workers";
export class MyDurableObject extends DurableObject<Env> {
constructor(ctx: DurableObjectState, env: Env) {
// Required, as we are extending the base class.
super(ctx, env)
}
async sayHello():Promise<string> {
let result = this.ctx.storage.sql
.exec("SELECT 'Hello, World!' as greeting")
.one();
return result.greeting;
}
}
export default {
async fetch(request, env, ctx): Promise<Response> {
const stub = env.MY_DURABLE_OBJECT.getByName(new URL(request.url).pathname);
const greeting = await stub.sayHello();
return new Response(greeting);
},
} satisfies ExportedHandler<Env>;from workers import DurableObject, handler, Response
from urllib.parse import urlparse
class MyDurableObject(DurableObject):
async def say_hello(self):
result = self.ctx.storage.sql.exec(
"SELECT 'Hello, World!' as greeting"
).one()
return result.greeting
class Default(WorkerEntrypoint):
async def fetch(self, request):
url = urlparse(request.url)
stub = self.env.MY_DURABLE_OBJECT.getByName(url.path)
greeting = await stub.say_hello()
return Response(greeting)このチュートリアルを終えると、次が完了しています。
- Durable Object を作成した
- RPC メソッド を呼び出して Durable Object と通信した
- Durable Object をグローバルにデプロイした
- Durable Object スタブを作成する
- Durable Objects のストレージにアクセスする
- Miniflare ↗ - Durable Objects のモックとテストに役立つツールです。