Cloudflare WorkersのService Bindingsを使って別Workerの関数を呼び出す

Cloudflare WorkersのService Bindingsを使って別Workerの関数を呼び出す

Cloudflare Workersで複数のWorker間の処理を共有したいとき、Service Bindingsという機能を使うと便利です。今回はRPCベースのService Bindingsを実装して、wranglerコマンドとcfコマンドの両方で試してみました。
2026.10.02

こんばんは、情報システム室の夏目です。

最近Cloudflare Workersで個人開発をしているのですが共通処理を別のWorkerに切り出したいと思いました。

Service Bindingsという機能を使えばできそうなので、今回はそれを試します。

1. Service Bindings

https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/

Service BindingsはCloudflare WorkersにBindingsの一つで、あるWorkerから別のWorkerをの処理を呼び出し、結果を受け取ることができます。
この際通信はパブリックインターネットに出ることなく、閉じたネットワークを通じて行われます。

Service BindingsではHTTPとRPCの、二つのインターフェースがあります。

  • HTTPベースの場合は別ワーカーの fetch()ハンドラ を呼び出し、結果を受け取ります
    • await env.BINDING_NAME.fetch(request) の形でRequestオブジェクトを送信し、Responseオブジェクトを受け取ります
  • RPCベースの場合は別ワーカーの関数を、通常の関数コールのように使えます
    • await env.BINDING_NAME.myMethod(arg1) のように呼び出し、結果を受け取ります

現在は、RPCベースのService Bindingが推奨されているので今回はこちらを試してみます。

2. RPCベースのService Bindingsを試してみる

呼び出されるWorkerを trial-inner-worker, trial-outer-worker として作成します。

2-1. 二つのプロジェクトを作成する

npm create cloudflare@latest コマンドで二つのプロジェクトを作成します。
また、作成時の選択肢は両方とも次のものを選んでいます

  • 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 add an AGENTS.md file to help AI coding tools understand Cloudflare APIs?: No
  • Do you want to use git for version control?: No
  • Do you want to deploy your application?: No
$ npm create cloudflare@latest trial-inner-worker
$ npm create cloudflare@latest trial-outer-worker

2-2. trial-inner-workerを作成する

Service Bindingsで呼び出される側のWorker trial-inner-worker を実装します。

最初にwrangler.jsoncを編集します。

trial-inner-worker/wrangler.jsonc
{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "trial-inner-worker",
  "main": "src/index.ts",
  "compatibility_date": "2026-09-26",
  "observability": {
    "enabled": true
  },
- "upload_source_maps": true
+ "upload_source_maps": true,
+ "workers_dev": false,
+ "preview_urls": false,
+ "vars": { "MY_VARIABLE": "production_value" }
}

Service Bindingsでのみ呼び出すつもりなので workers_dev: false, preview_urls: false としてCloudflareがアクセス用のURLを払い出さないようにしています。

また、こちらのWorkerが呼び出されているとわかるように vars を設定しています。

wrangler.jsoncを編集したので型ファイルを生成し直します。

$ cd trial-inner-worker
$ npx wrangler types

型ファイルを生成し直したのでWorkerの実装を行います。

trial-inner-worker/src/index.ts
import { WorkerEntrypoint, env } from "cloudflare:workers";

export default class Worker extends WorkerEntrypoint {
  // Worker用のハンドラーがないとデプロイできないので使わないけど置いておく
  async fetch() {
    return new Response(null, { status: 404 });
  }

  message(): string {
    return env.MY_VARIABLE;
  }
}

RPCベースのService Bindingsの場合、 WorkerEntrypoint というクラスを継承したクラスをDefault Exportします。
Worker用のハンドラーがないとデプロイ時にエラーになるので、書いておきます。

message(): string は今回trial-outer-workerから呼び出す関数です。
今回はenvから値を受け取りそれを返します。
また、この関数では返り値の型を明記します。

実装したのでデプロイします。

$ npx wrangler deploy

2-3. trial-outer-workerを作成する

Service Bindingsで呼び出す側のWorkerを実装します。

最初はwrangler.jsoncを編集します。

trial-outer-worker/wrangler.jsonc
{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "trial-outer-worker",
  "main": "src/index.ts",
  "compatibility_date": "2026-09-26",
  "observability": {
    "enabled": true
  },
- "upload_source_maps": true
+ "upload_source_maps": true,
+ "services": [{ "binding": "MY_SERVICE", "service": "trial-inner-worker" }]
}

Service Bindingsは services というキーで設定します。
呼び出したいWorkerを配列で列挙します。

列挙する際、 bindings に呼び出す際に使う名前を、 service に呼び出したいWorkerの名前を記載します。

wrangler.jsoncを編集したので、型ファイルを再生成します。

$ cd ../trial-inner-worker
$ npx wrangler types -c ./wrangler.jsonc -c ../trial-inner-worker/wrangler.jsonc

Service Bindingsを使用する場合、 -c オプションを使って複数のwrangler.jsoncを指定します。
自身のwrangler.jsoncと呼び出したいWorkerのwrangler.jsoncを全て列挙します。

このとき、 ../trial-inner-worker/wrangler.jsonc だけがあればいいわけではありません。
../trial-inner-worker/wrangler.jsonc で設定しているentry-point fileがないとエラーになります。

ちなみに別枠で検証したところ、必要なのはentry-point fileだけのようです。
entry-point fileだけを作成して、そのファイルの中で存在しないtsファイルをimportするように書いたときでも型ファイルの生成は成功しました。

型ファイルを再生成したのでWorkerの実装をします。

trial-outer-worker/src/index.ts
export default {
  async fetch(request, env, ctx): Promise<Response> {
    const message = await env.MY_SERVICE.message()
    return new Response(message);
  },
} satisfies ExportedHandler<Env>;

MY_SERVICE という名前でバインディングしているので await env.MY_SERVICE.message() で呼び出します。

自分はWebStormをTypeScriptのIDEとして使っているのですが、補完もきちんと効きました。
ちなみに、trial-inner-workerで message() の返り値の型を明記していなかったとき、型推論は any になっていました。
今回は message(): string と明記しているので型推論は string になっています。


返り値の型を明記しているとき


返り値の型を記載していないとき

実装できたのでデプロイします。

$ npx wrangler deploy
npm notice run trial-outer-worker@0.0.0 npx
npm notice run 'wrangler' deploy

 ⛅️ wrangler 4.146.0
────────────────────
Total Upload: 0.23 KiB / gzip: 0.18 KiB
Worker Startup Time: 1 ms
Your Worker has access to the following bindings:
Binding                                  Resource      
env.MY_SERVICE (trial-inner-worker)      Worker        

Uploaded trial-outer-worker (1.17 sec)
Deployed trial-outer-worker triggers (0.66 sec)
  https://trial-outer-worker.luciferous.workers.dev
Current Version ID: 38b133c8-497e-456f-9652-7bc850de27dc

2-4. trial-outer-worker を叩いてみる

curlで叩いてみます。

$ curl https://trial-outer-worker.luciferous.workers.dev
production_value

production_value という値はtrial-inner-workerでしか設定していない値なので、trial-inner-workerの関数が実行されたのがわかります。

3. cf コマンドで管理されたプロジェクトではどう書くのかを試してみる

最近cfコマンドというwranglerとは別のCloudflare CLIは発表されました。
まだbetaではありますが、こちらだとどうなるのか試してみます。

作成するWorkerは trial-cf-inner-worker と trial-cf-outer-worker の二つです。

3-1. 二つのプロジェクトを作成する

npx cf init コマンドでプロジェクトを作成します。

また、作成時の選択肢は両方とも次のものを選んでいます

  • Which package manager do you want to use?: npm
$ npx cf init trial-cf-inner-worker
$ npx cf init trial-cf-outer-worker

3-2. trial-cf-inner-worker を作成する

Service Bindingsで呼び出される側のtrial-cf-inner-workerを実装します。
基本的にはtrial-inner-workerと同様になるように実装します。

設定ファイルであるcloudflare.config.tsを編集します。

trial-cf-inner-worker/cloudflare.config.ts
 import { bindings, defineConfig } from "cf/config";

  import * as entrypoint from "./src/index.ts" with { type: "cf-worker" };

-export default defineConfig({
-  worker: {
-    name: "trial-cf-inner-worker",
-    compatibilityDate: "2026-09-30",
-    entrypoint,
-    env: {
-      WORLD: bindings.text("World"),
-    },
+export const worker = {
+  name: "trial-cf-inner-worker",
+  compatibilityDate: "2026-09-30",
+  entrypoint,
+  workersDev: false,
+  previewUrls: false,
+  env: {
+    MY_VARIABLE: bindings.text("production_value"),
   },
-});
+};
+
+export default defineConfig({ worker });

trial-cf-outer-workerで使用するので export const worker に切り出します。

trial-inner-workerと同じ名前で同じ値のenvになるようにしています。

設定ファイルを変更したので型ファイルを再生成し、Workerを実装します。

$ npx cf workers types
trial-cf-inner-worker/src/index.ts
import { WorkerEntrypoint, env } from "cloudflare:workers";

export default class Worker extends WorkerEntrypoint {
  // Worker用のハンドラーがないとデプロイできないので使わないけど置いておく
  async fetch() {
    return new Response(null, { status: 404 });
  }

  message(): string {
    return env.MY_VARIABLE;
  }
}

コードを実装したので、デプロイをします。

$ npx cf deploy

3-3. trial-cf-outer-worker を作成する

Service Bindingsで呼び出す側のWorkerを実装します。
こちらもtrial-outer-workerと同様になるように実装します。

設定ファイルであるcloudflare.config.tsを編集します。

trial-cf-outer-worker/cloudflare.config.ts
 import { bindings, defineConfig } from "cf/config";

+import { worker } from "../trial-cf-inner-worker/cloudflare.config.ts";
+
 import * as entrypoint from "./src/index.ts" with { type: "cf-worker" };

 export default defineConfig({
   worker: {
     name: "trial-cf-outer-worker",
     compatibilityDate: "2026-09-30",
     entrypoint,
     env: {
-      WORLD: bindings.text("World"),
+      MY_SERVICE: bindings.worker({ worker }),
     },
   },
 });

cloudflare.config.tsではService Bindingsを設定する際にはenvに MY_SERVICE: bindings.worker({ worker }) のように書きます。
このために、trial-cf-inner-workerで export const worker としました。

一応 bindings.worker({ worker: "trial-cf-inner-worker" }) と書くこともできます。
それでもバインディングは正常に作成されるのですが、型ファイルを生成しても実装した関数まで補完が効きません。

設定ファイルであるcloudflare.config.tsを編集したので型ファイルを再生成し、コードを実装します。

$ npx cf workers types
trial-cf-outer-worker/src/index.ts
export default {
  async fetch(request, env, ctx): Promise<Response> {
    const message = await env.MY_SERVICE.message()
    return new Response(message);
  },
} satisfies ExportedHandler<Env>;

コードを実装したのでデプロイします。

$ npx cf deploy
npm notice run npx
npm notice run 'cf' deploy
🍊☁️  cf · v1.0.0-beta.10 · update available: v1.0.0-beta.11
────────────────────────────────────────────────────────────
├  Build
│  Delegating to npx vite build
npm notice run npx
npm notice run 'vite' build
vite v8.3.2 building ssr environment for production...
✓ 4 modules transformed.
computing gzip size...
.cloudflare/output/v0/workers/default/bundle/.vite/manifest.json  0.15 kB │ gzip: 0.11 kB
.cloudflare/output/v0/workers/default/bundle/index.js             0.26 kB │ gzip: 0.20 kB

✓ built in 12ms
◆  Build complete
│
├  Deploy
│  Total Upload: 0.25 KiB / gzip: 0.20 KiB
│  Worker Startup Time: 1 ms
│  Your Worker has access to the following bindings:
│  Binding                                     Resource      
│  env.MY_SERVICE (trial-cf-inner-worker)      Worker        
│
│  Uploaded trial-cf-outer-worker (1.24 sec)
│  Deployed trial-cf-outer-worker triggers (0.83 sec)
│    https://trial-cf-outer-worker.luciferous.workers.dev
│  Current Version ID: 4b811401-3439-4fde-b1a4-17c654b8b92d
│
◆  Deploy complete

3-4. trial-cf-outer-workerを叩いてみる

curlで叩いてみます。

$ curl https://trial-cf-outer-worker.luciferous.workers.dev
production_value

cfコマンドで作成した場合でもうまく動きました。

4. まとめ

以上、Cloudflare WorkersでService Bindingsを試してみる話でした。

RPCベースのService Bindingsは思った以上に便利そうです。

何かのお役に立てたら幸いです。

この記事をシェアする

関連記事