Cloudflare Workflowsを使ってみる ~その1 セットアップとワークフロー作成~

はじめに

Cloudflareが提供する「Cloudflare Workflows」を使ってみた。
複数のステップを持つ処理を実装する際、エラーハンドリングやステートの管理が煩雑になることがある。
外部APIを呼び出して、その結果を待って次の処理を実行し、さらに別のAPIを呼ぶ… といった処理を書く場合、途中でエラーが発生したらどこから再開すべきか、どのステップまで完了したかを管理する必要がある。

こうした課題を解決する「Durable Execution(耐久実行)」や「ワークフローオーケストレーション」は、各クラウドプロバイダでも提供されている。

クラウドプロバイダサービス名
AWSStep Functions
AzureDurable Functions
GCPWorkflows
CloudflareWorkflows

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設定ファイル
└── .gitignore

2. 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つのステップで構成されている。

  1. fetch data: Cloudflare APIからIPアドレス情報を取得
  2. pause: 20秒間スリープ
  3. 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-01

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.dostep.sleepstep.waitForEventといったAPIで、複雑な処理フローを表現できる。
    TypeScriptの型サポートもあるので良い。

  • スケールについて
    Cloudflareのグローバルネットワーク上で動作し、自動的にスケールする。
    インフラの管理が不要というところが大きなメリット。

  • 統合の容易さ
    Workers、R2、KV、D1、Workers AIなど、Cloudflareの他のサービスと連携できる。
    既存のWorkersプロジェクトに組み込める。

参考

おわりに

Cloudflare Workflowsのセットアップから最初のワークフローの実行まで試してみた。

ステートの永続化、自動リトライ、長時間のスリープなど、耐久実行に必要な機能が標準で提供されている。
複雑な処理フローを簡潔に記述でき、インフラ管理不要でスケーラブルに動作する点が良い。

生成AIを使った

  • 音声認識→テキスト変換→議事録作成
  • 画像アップロード→サムネイル生成→メタデータ抽出
  • 動画アップロード→字幕生成→翻訳
  • PDFアップロード→OCR→要約

このあたりのステップ処理にも効いてきそうだなと感じた。
※PDFの奴はNotebookLMとかでもいいかもだが、まあアプリに組み込むという点で…!

これもそうなんだけど、AWSのサーバレス系サービスももう少し使ってみたい。
個人開発的な部分を整備したいのだ・・・!

Hugo で構築されています。
テーマ StackJimmy によって設計されています。