はじめに
Cloudflareが提供する「Cloudflare Workflows」を使ってみた。
複数のステップを持つ処理を実装する際、エラーハンドリングやステートの管理が煩雑になることがある。
外部APIを呼び出して、その結果を待って次の処理を実行し、さらに別のAPIを呼ぶ… といった処理を書く場合、途中でエラーが発生したらどこから再開すべきか、どのステップまで完了したかを管理する必要がある。
こうした課題を解決する「Durable Execution(耐久実行)」や「ワークフローオーケストレーション」は、各クラウドプロバイダでも提供されている。
| クラウドプロバイダ | サービス名 |
|---|---|
| AWS | Step Functions |
| Azure | Durable Functions |
| GCP | Workflows |
| Cloudflare | Workflows |
Cloudflare Workflowsは、こうした「Durable Execution(耐久実行)」を実現するプラットフォームとなっており、Cloudflare Workers上で動作する。
ステップごとに自動的にステートを永続化し、失敗時は自動リトライ、長時間のスリープやイベント待機も可能となっている。
Cloudflare Workflowsのセットアップから、最初のワークフローの作成・デプロイまでを実践してみる。
環境
Ubuntu 24.04 LTS
Node.js v20.11.0
npm 10.2.4前提条件
Cloudflare Workflowsを使用するには以下が必要となる。
- Cloudflareアカウント(無料プランでも利用可能)
- Node.jsのインストール
Cloudflareアカウントの作成
※省略する
プロジェクトの作成
1. Workerプロジェクトの初期化
create cloudflare CLIツールを使用してプロジェクトを作成する。
npm create cloudflare@latest my-workflow対話式のセットアップが始まる。以下のように選択する。
◇ What would you like to start with?
│ Hello World example
│
◇ Which template would you like to use?
│ Worker only
│
◇ 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 (we will be making some changes before deploying)ログ
Need to install the following packages:
[email protected]
Ok to proceed? (y) y
> npx
> create-cloudflare my-workflow
──────────────────────────────────────────────────────────────────────────────────────────────────────────
👋 Welcome to create-cloudflare v2.63.0!
🧡 Let's get started.
📊 Cloudflare collects telemetry about your usage of Create-Cloudflare.
Learn more at: https://github.com/cloudflare/workers-sdk/blob/main/packages/create-cloudflare/telemetry.md
──────────────────────────────────────────────────────────────────────────────────────────────────────────
╭ Create an application with Cloudflare Step 1 of 3
│
├ In which directory do you want to create your application?
│ dir ./my-workflow
│
├ What would you like to start with?
│ category Hello World example
│
├ Which template would you like to use?
│ type Worker only
│
├ Which language do you want to use?
│ lang TypeScript
│
├ Copying template files
│ files copied to project directory
│
├ Updating name in `package.json`
│ updated `package.json`
│
├ Installing dependencies
│ installed via `npm install`
│
╰ Application created
╭ Configuring your application for Cloudflare Step 2 of 3
│
├ Installing wrangler A command line tool for building Cloudflare Workers
│ installed via `npm install wrangler --save-dev`
│
├ Retrieving current workerd compatibility date
│ compatibility date 2026-02-05
│
├ Generating types for your application
│ generated to `./worker-configuration.d.ts` via `npm run cf-typegen`
│
├ Installing @types/node
│ installed via npm
│
├ Do you want to use git for version control?
│ yes git
│
├ Initializing git repo
│ initialized git
│
├ Committing new files
│ git commit
│
╰ Application configured
╭ Deploy with Cloudflare Step 3 of 3
│
├ Do you want to deploy your application?
│ no deploy via `npm run deploy`
│
╰ Done
────────────────────────────────────────────────────────────
🎉 SUCCESS Application created successfully!
💻 Continue Developing
Change directories: cd my-workflow
Deploy: npm run deploy
📖 Explore Documentation
https://developers.cloudflare.com/workers
🐛 Report an Issue
https://github.com/cloudflare/workers-sdk/issues/new/choose
💬 Join our Community
https://discord.cloudflare.com
────────────────────────────────────────────────────────────プロジェクトディレクトリに移動する。
cd my-workflow生成されたファイル
my-workflow/
├── src/
│ └── index.ts # メインのWorkerコード
├── node_modules/
├── package.json # 依存関係の定義
├── tsconfig.json # TypeScript設定
├── wrangler.jsonc # Wrangler設定ファイル
└── .gitignore2. Workflowの実装
src/workflow.tsという新しいファイルを作成する。
touch src/workflow.ts以下のコードを記述する。
import { WorkflowEntrypoint, WorkflowStep } from "cloudflare:workers";
import type { WorkflowEvent } from "cloudflare:workers";
type Params = { name: string };
type IPResponse = { result: { ipv4_cidrs: string[] } };
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
// ステップ1: データの取得
const data = await step.do("fetch data", async () => {
const response = await fetch("https://api.cloudflare.com/client/v4/ips");
return await response.json<IPResponse>();
});
// ステップ2: 一時停止(20秒)
await step.sleep("pause", "20 seconds");
// ステップ3: データの処理
const result = await step.do(
"process data",
{ retries: { limit: 3, delay: "5 seconds", backoff: "linear" } },
async () => {
return {
name: event.payload.name,
ipCount: data.result.ipv4_cidrs.length,
};
},
);
return result;
}
}このワークフローは3つのステップで構成されている。
- fetch data: Cloudflare APIからIPアドレス情報を取得
- pause: 20秒間スリープ
- process data: 取得したデータを処理(3回までリトライ)
各ステップは自動的に永続化され、失敗した場合は完了済みのステップから再開される。
3. Workflowの設定
wrangler.jsoncを開き、workflows設定を追加する。
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-workflow",
"main": "src/index.ts",
"compatibility_date": "2026-02-05",
"observability": {
"enabled": true
},
"workflows": [
{
"name": "my-workflow",
"binding": "MY_WORKFLOW",
"class_name": "MyWorkflow"
}
]
}設定項目の説明
| 項目 | 説明 |
|---|---|
name | ワークフローの名前 |
binding | コード内でワークフローにアクセスする際の変数名 |
class_name | エクスポートしたワークフロークラス名 |
型定義を生成する。
npx wrangler typesこれにより、worker-configuration.d.tsファイルが生成され、Env型にMY_WORKFLOWバインディングが含まれる。
ログ
⛅️ wrangler 4.63.0
───────────────────
Generating project types...
declare namespace Cloudflare {
interface GlobalProps {
mainModule: typeof import("./src/index");
}
interface Env {
MY_WORKFLOW: Workflow /* MyWorkflow */;
}
}
interface Env extends Cloudflare.Env {}
Generating runtime types...
Runtime types generated.
────────────────────────────────────────────────────────────
✨ Types written to worker-configuration.d.ts
📖 Read about runtime types
https://developers.cloudflare.com/workers/languages/typescript/#generate-types
📣 Remember to rerun 'wrangler types' after you change your wrangler.jsonc file.4. APIエンドポイントの実装
src/index.tsを以下の内容に置き換える。
export { MyWorkflow } from "./workflow";
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
const instanceId = url.searchParams.get("instanceId");
// インスタンスIDが指定されている場合はステータスを返す
if (instanceId) {
const instance = await env.MY_WORKFLOW.get(instanceId);
return Response.json(await instance.status());
}
// 新しいワークフローインスタンスを作成
const instance = await env.MY_WORKFLOW.create();
return Response.json({ instanceId: instance.id });
},
} satisfies ExportedHandler<Env>;このコードは2つの機能を提供する。
GET /: 新しいワークフローインスタンスを作成し、IDを返すGET /?instanceId=xxx: 指定されたインスタンスのステータスを返す
ローカルでの動作確認
開発サーバーの起動
npx wrangler devログ
⛅️ wrangler 4.63.0
───────────────────
Cloudflare collects anonymous telemetry about your usage of Wrangler. Learn more at https://github.com/cloudflare/workers-sdk/tree/main/packages/wrangler/telemetry.md
Your Worker has access to the following bindings:
Binding Resource Mode
env.MY_WORKFLOW (MyWorkflow) Workflow local
❓ Your types might be out of date. Re-run `wrangler types` to ensure your types are correct.
╭──────────────────────────────────────────────────────────────────────╮
│ [b] open a browser [d] open devtools [c] clear console [x] to exit │
╰──────────────────────────────────────────────────────────────────────╯
⎔ Starting local server...
[wrangler:info] Ready on http://localhost:8787ワークフローインスタンスの作成
別のターミナルを開き、ワークフローを起動する。
curl http://localhost:8787レスポンス:
{
"instanceId": "abc-123-def"
}ステータスの確認
返されたインスタンスIDを使用してステータスを確認する。
curl "http://localhost:8787?instanceId=abc-123-def"ワークフローは3つのステップを順番に実行する。
20秒のスリープ後、最終的に完了状態になる。
# 20秒後に再度確認
curl "http://localhost:8787?instanceId=abc-123-def"レスポンス例
{
"status": "complete",
"output": {
"name": "test",
"ipCount": 14
},
"steps": [
{
"name": "fetch data",
"status": "success"
},
{
"name": "pause",
"status": "success"
},
{
"name": "process data",
"status": "success"
}
]
}デプロイ
ローカルでの動作確認ができたら、本番環境にデプロイする。
npx wrangler deployログ
Successfully logged in.
Total Upload: 20.70 KiB / gzip: 5.17 KiB
Worker Startup Time: 19 ms
Your Worker has access to the following bindings:
Binding Resource
env.MY_WORKFLOW (MyWorkflow) Workflow
Uploaded my-workflow (5.15 sec)
Deployed my-workflow triggers (3.10 sec)
https://my-workflow.xxxxxxxx.workers.dev
workflow: my-workflow
Current Version ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
🪵 Logs were written to "/home/kbushi/.config/.wrangler/logs/wrangler-2026-02-07_08-03-30_770.log"デプロイされたURLに対して同じようにリクエストを送信できる。
# ワークフローインスタンスを作成
curl https://my-workflow.your-subdomain.workers.dev
# ステータスを確認
curl "https://my-workflow.your-subdomain.workers.dev?instanceId=abc-123-def"Wrangler CLIでのインスタンス確認
デプロイ後、Wrangler CLIを使用してワークフローインスタンスの詳細を確認できる。
npx wrangler workflows instances describe my-workflow latest出力例
Instance ID: abc-123-def
Status: complete
Started: 2026-01-28T03:30:00.000Z
Completed: 2026-01-28T03:30:23.000Z
Steps:
1. fetch data
Status: success
Duration: 245ms
2. pause
Status: success
Sleep duration: 20s
3. process data
Status: success
Duration: 12ms
Retries: 0/3
Output:
{
"name": "test",
"ipCount": 14
}この出力から以下の情報が確認できる。
- 各ステップの成功/失敗ステータス
- ステップごとの実行時間
- スリープの状態と継続時間
- リトライ回数
- エラーメッセージ(失敗時)
コントロールパネルからの確認
Cloudflareダッシュボードの「Workers」→「Workflows」セクションからもインスタンスの状態を確認できる。

Workflowsの機能についてのメモ
Durable Execution(耐久実行)
各ステップの結果は自動的に永続化される。
途中でエラーが発生しても、完了したステップは再実行されず、失敗したステップから再開される。
// このステップが成功すれば、結果は永続化される
const data = await step.do("fetch data", async () => {
return await fetchExpensiveData();
});
// 次のステップが失敗しても、上のステップは再実行されない
const result = await step.do("process", async () => {
return processData(data); // dataは永続化された値
});スリープとスケジューリング
ワークフローを数秒から数日間一時停止できる。
// 相対的な期間
await step.sleep("wait 1 hour", "1 hour");
// 絶対的な時刻まで
await step.sleepUntil("wait until midnight", new Date("2026-01-29T00:00:00Z"));自動リトライ
ステップごとにリトライポリシーを設定できる。
await step.do(
"call external API",
{
retries: {
limit: 5, // 最大5回リトライ
delay: "10 seconds", // 各リトライの間隔
backoff: "exponential" // 指数バックオフ
}
},
async () => {
return await callExternalAPI();
}
);外部イベントの待機
Webhookやユーザー入力などの外部イベントを待つこともできる。
await step.waitForEvent("await approval", {
event: "approved",
timeout: "24 hours"
});ユースケース
Cloudflare Workflowsは以下のようなシナリオに適している。
データパイプライン処理
ファイルアップロード後の画像処理、メタデータ抽出、サムネイル生成などを順次実行。
AI処理のチェーン
Workers AIで画像認識→テキスト生成→翻訳→保存といった複数のAI処理を連携。
ユーザーライフサイクル管理
トライアル期間の管理、リマインダーメールの送信、期限切れ時の処理を自動化。
承認フロー
管理者の承認を待って次の処理を実行するワークフロー。
長時間バッチ処理
数時間かかるデータ処理を、タイムアウトなしで実行。
所感
Cloudflare Workflowsを使ってみて、マルチステップ処理が簡潔に書けることに驚いた。
特に以下の点が気に入った。
ステート管理について
よくあるものとして、外部ストレージにステートを保存し、エラー時の復旧処理を自分で書く必要があった。
Workflowsではstep.doで囲むだけで自動的に永続化され、失敗時は完了したステップから再開される。API
step.do、step.sleep、step.waitForEventといったAPIで、複雑な処理フローを表現できる。
TypeScriptの型サポートもあるので良い。スケールについて
Cloudflareのグローバルネットワーク上で動作し、自動的にスケールする。
インフラの管理が不要というところが大きなメリット。統合の容易さ
Workers、R2、KV、D1、Workers AIなど、Cloudflareの他のサービスと連携できる。
既存のWorkersプロジェクトに組み込める。
参考
Cloudflare Workflows Documentation
https://developers.cloudflare.com/workflows/Build your first Workflow
https://developers.cloudflare.com/workflows/get-started/guide/Workflows REST API
https://developers.cloudflare.com/api/resources/workflows/
おわりに
Cloudflare Workflowsのセットアップから最初のワークフローの実行まで試してみた。
ステートの永続化、自動リトライ、長時間のスリープなど、耐久実行に必要な機能が標準で提供されている。
複雑な処理フローを簡潔に記述でき、インフラ管理不要でスケーラブルに動作する点が良い。
生成AIを使った
- 音声認識→テキスト変換→議事録作成
- 画像アップロード→サムネイル生成→メタデータ抽出
- 動画アップロード→字幕生成→翻訳
- PDFアップロード→OCR→要約
このあたりのステップ処理にも効いてきそうだなと感じた。
※PDFの奴はNotebookLMとかでもいいかもだが、まあアプリに組み込むという点で…!
これもそうなんだけど、AWSのサーバレス系サービスももう少し使ってみたい。
個人開発的な部分を整備したいのだ・・・!