Cloudflare WorkersのCron TriggerをLocal Explorerから実行してみた

Cloudflare WorkersのCron TriggerをLocal Explorerから実行してみた

Cloudflare WorkersのCron TriggerをLocal Explorerから単発実行してみました。設定済みの式と一時的な式での実行、予定時刻の指定、noRetryの表示を確認し、Cloudflare管理画面での定期実行設定との違いも画面付きで紹介します。
2026.10.21

どうも!オペ部の西村祐二です!

Cloudflare WorkersでCron処理を作ったとき、デプロイ前にscheduled()の動作を画面から試したくなることがあります。

2026年10月17日のアップデートで、Local ExplorerからCron Triggerを単発実行できるようになりました。設定済みのCron式を選んでTriggerを押すほか、その場で別の式や予定時刻を指定できます。

今回は最小のWorkerを用意し、正常終了と意図的な例外の両方を試しました。Cloudflare管理画面のCron設定画面も確認したので、用途の違いとあわせて紹介します。

管理画面のCron設定とLocal Explorerの違い

Cloudflare管理画面は、デプロイ済みWorkerを定期実行するCronスケジュールの設定場所です。Local Explorerは、ローカル開発中のWorkerのscheduled()を1回ずつ呼び出すために使います。

次は自作した概念図です。管理画面で設定するクラウドの定期実行と、今回試すローカルでの単発実行を分けて描いています。

自作したCron Triggerの実行先の概念図。管理画面の設定はクラウドの定期実行に、Local ExplorerのTriggerはローカルWorkerの単発実行につながる。

管理画面では、Workers & Pagesから既存の検証用Workerを選び、設定→トリガーイベント→Cron トリガーの追加を開きました。Cron 式タブに0 * * * *を入れると、今後の予定としてUTCの毎時00分が表示されます。

Cloudflare管理画面のCron式入力ダイアログ。0 * * * *に対してUTCの毎時00分の予定が表示されている。

これは既存の検証用Workerの画面です。以下で作るローカルのサンプルとは別で、撮影後はキャンセルで閉じ、クラウド側への保存・実行は行っていません。画像内のアカウント情報などは伏せています。

なお、Wranglerで管理するWorkerでは、Cron TriggerもWrangler設定ファイルで管理することが公式ドキュメントで推奨されています。このあと作るサンプルでもwrangler.jsoncに式を定義します。

最小のWorkerを用意する

Cronのテスト機能は、Wrangler 4.139.0以降、またはCloudflare Vite plugin 1.60.0以降が対象です。今回はWranglerを使い、次の環境で確認しました。

項目 バージョン
macOS 26.6.1
Node.js 24.21.0
pnpm 12.14.0
Wrangler 4.151.3

新しい作業ディレクトリに、次のファイルを用意しました。Wranglerのバージョンは再現用に固定しています。

package.json
{
  "name": "local-explorer-cron-demo",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "packageManager": "pnpm@12.14.0",
  "scripts": {
    "dev": "wrangler dev --port 8789"
  },
  "devDependencies": {
    "wrangler": "4.151.3"
  }
}

pnpmが実行する依存パッケージのインストール処理は、このディレクトリの設定で許可しました。

pnpm-workspace.yaml
allowBuilds:
  esbuild: true
  workerd: true
pnpm install
mkdir -p src

wrangler.jsoncでは毎時00分の式を設定します。observability.enabledも有効にしています。

wrangler.jsonc
{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "local-explorer-cron-demo",
  "main": "src/index.js",
  "compatibility_date": "2026-10-17",
  "triggers": {
    "crons": ["0 * * * *"]
  },
  "observability": {
    "enabled": true
  }
}

Workerは受け取ったCron式と予定時刻をログに出すだけにしました。後半の5 * * * *の分岐は、noRetry()の表示と例外を確認するためのものです。外部APIやデータベースへのアクセスはありません。

src/index.js
export default {
  async fetch() {
    return new Response("Open /cdn-cgi/local/explorer to test Cron Triggers.\n");
  },
  async scheduled(controller) {
    const event = {
      cron: controller.cron,
      scheduledTime: controller.scheduledTime,
      scheduledAt: new Date(controller.scheduledTime).toISOString(),
    };
    console.log("cron-event", JSON.stringify(event));
    if (controller.cron === "5 * * * *") {
      controller.noRetry();
      console.log("noRetry requested");
      throw new Error("Intentional failure after noRetry");
    }
    console.log("cron-completed");
  },
};

次のコマンドでローカル開発サーバーを起動しました。ここではポート8789を使っています。

pnpm exec wrangler dev --local --ip 127.0.0.1 --port 8789

ブラウザでhttp://127.0.0.1:8789/cdn-cgi/local/explorerを開きます。別のポートで起動した場合は、URLのポートも合わせます。

設定済みのCron式を時刻指定で実行する

左メニューのCron Triggers→Configured Cronsを開くと、wrangler.jsoncに設定した0 * * * *が表示されました。

Scheduled timeはNowのままでも実行できます。今回はハンドラへ渡る時刻を固定するため、Add presetを開きました。Date and time (UTC)に2026-10-21T00:00を入力し、Save presetで保存しました。日本時間では2026年10月21日09:00です。

その時刻を選び、式の横のTriggerを押すと、次の結果になりました。

Configured Cronsの実行結果。Outcome: okと、指定したCron式および予定時刻が表示されている。

画面では、Outcome: ok、noRetry() was not requested.、呼び出したCron式、指定した予定時刻を確認できました。Wranglerを起動したターミナルにも、次のログが出ています。

実行ログ
cron-event {"cron":"0 * * * *","scheduledTime":1792540800000,"scheduledAt":"2026-10-21T00:00:00.000Z"}
cron-completed

scheduledTimeの値はミリ秒単位のUnix時刻です。画面で選んだUTCの日時と、ハンドラが受け取った値が一致しています。

ここでの時刻指定は、その時刻まで実行を待つ予約ではありません。Triggerを押すとその場で実行され、指定値がハンドラへ渡ります。日付境界など、時刻によって分岐する処理の入力をそろえて試すときに使えます。

設定を書き換えずに別のCron式を試す

次にCron Triggers→Ad-Hoc Triggersを開き、Add triggerからExpressionで15 * * * *を入力しました。これはwrangler.jsoncに登録していない式です。

Scheduled timeで先ほどのプリセットを選び直し、Triggerを押すと、こちらもOutcome: okになりました。

Ad-Hoc Triggersで設定にない15 * * * *を実行した結果。Outcome: okと入力した式が表示されている。

ログのcontroller.cronに相当する値も、入力した15 * * * *になっています。今回指定した時刻は00:00ですが、式で指定した毎時15分になるまで待たずに呼び出せました。

実行後もwrangler.jsoncの式は0 * * * *だけのままでした。アドホックの式と時刻プリセットはブラウザに保存されるもので、Wranglerの設定やデプロイ済みのCron Triggerは変更しません。

例外とnoRetryの状態を確認する

同じ入力欄を5 * * * *に変え、もう一度Triggerを押しました。サンプルコードでは、この式のときだけcontroller.noRetry()を呼び出したあとに例外を投げます。

意図的な例外を発生させた実行結果。Outcome: exceptionとnoRetry() was requestedが表示されている。

結果はOutcome: exceptionになり、noRetry() was requested; this remains a one-off local test.と表示されました。正常終了の画面と比べると、処理の成否とnoRetry()を呼んだかどうかを分けて確認できます。

今回確認したのは、この単発のローカル実行での表示です。クラウド上でのリトライ回数・間隔や、noRetry()を呼んだあとの実際の再実行までは検証していません。

試すときに押さえておきたい点

まとめ

Local Explorerから、設定済みのCron式と一時的な式の両方でscheduled()を実行できました。予定時刻を固定できるので、同じ入力で処理を繰り返し確かめられます。

Cloudflare管理画面はクラウドの定期実行設定に、Local Explorerはデプロイ前の単発テストに使えます。今回のような正常終了・例外・noRetry()の呼び出し状態は、まずローカルの画面で確認してからクラウドの動作確認へ進められそうです。

参考

この記事をシェアする

関連記事