Cloudflare WorkersでSlack Appのバックエンドを動かす

Cloudflare WorkersでSlack Appのバックエンドを動かす

Cloudflare WorkersでSlack Appのバックエンドを動かす方法をご紹介します。Bolt for JavaScriptが使えない理由から、`slack-edge`ライブラリを使った実装、Event Subscriptionsの設定まで、実際の手順に沿って解説していきます。
2026.08.13

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

最近個人的にCloudflare Workersで色々遊んでいます。
今回はSlack AppでEvent Subscriptionを行い、バックエンドをCloudflare Workersで動かしてみようと思います。

Bolt for JavaScriptを使うことはできない

Cloudflare WorkersではJSを動かすことができるのですが、Bolt for JavaScriptは使えないようでした。

https://zenn.dev/seratch/articles/c370cf8de7f9f5#cloudflare-workers-では-bolt-を使えないの?

https://zenn.dev/seratch/articles/c370cf8de7f9f5#cloudflare-workers-では-bolt-を使えないの?

ただ、残念ながら Bolt for JS は axios やその他 Node.js の API に依存する部分が多くあり、そのままでは Cloudflare Workers で使うことはできません。名前は for JavaScript ですが、どちらかというと for Node.js といった趣のフレームワークなのです。

また、Bolt for JS は Receiver というインターフェースで拡張する仕様になっているのですが、この点が Cloudflware Workers の fetch 関数としてリクエストを処理する仕様との相性が良くないという問題も存在しています。この点は Next.js や Nest.js のようなモダンなフレームワークを使った Web アプリの一部として動かしたいという場合にも制約となります。

Cloudflare WorkersでBolt for JavaScriptを使用することができません。

リクエスト署名の検証やリクエストペイロードの形式チェックなどを自前で実装すれば、Cloudflare Workersで動かすことはできなくはないですが、非常に面倒です。

そのため、今回は引用したブログにある slack-edge ライブラリを利用してCloudflare Workersで動かしてみます。

slack-edgeslack-cloudflare-workers

https://github.com/slack-edge/slack-edge

https://github.com/slack-edge/slack-cloudflare-workers

Cloudflare Workersと Vercel Edge Functions 向けのSlackアプリ開発フレームワークです。

slack-edge ライブラリに基本的な機能が入っています。
また、Bolt for PythonのLazyListenersに相当する機能が追加されているので使い勝手も良さそうです (Bolt for JavaScriptには相当する機能がない)。
slack-cloudflare-workers にはCloudflare KVを使って (インストール情報の管理など) OAuthフロー関連の実装が追加で入っています。

なので使い分けとしては、

  • 基本的には slack-edge
  • OAuthフロー関連をCloudflare KVで管理するなら slack-cloudflare-workers

ということになります。

またCloudflare WorkersやVercel Edge Functions向けとは書きましたが、コンテナサービスで動くアプリにも使用できます (DenoやBunにも対応)。

作って、動かしてみる

今回はSlack AppのEvent Subscriptionsでチャンネルにメッセージが投稿されたのを、 slack-edge を使って受け取るCloudflare Workersに作ってみます。

1. Workerを作成して、ほぼそのままデプロイする

Event Subscriptions用のURLを取得するためにWorkerの雛型に最小限の変更を加えて、Cloudflareにデプロイします。

1-1. npm create cloudflare@latest でWorkerの雛型を作る

$ npm create cloudflare@latest
npm notice run npx
npm notice run 'create-cloudflare'

──────────────────────────────────────────────────────────────────────────────────────────────────────────
👋 Welcome to create-cloudflare v2.71.1!
🧡 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 ./worker-subscribe-channel-messages

├ 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`

├ Do you want to add an AGENTS.md file to help AI coding tools understand Cloudflare APIs?
│ no agents

╰ 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-08-13

├ 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?
│ no git

╰ 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 worker-subscribe-channel-messages
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
────────────────────────────────────────────────────────────

今回は worker-subscribe-channel-messages という名前のWorkerを作成します。

1-2. デプロイスクリプトを修正する

package.json
{
  "scripts": {
    "cf-typegen": "wrangler types",
    "deploy": "npm run cf-typegen && wrangler deploy"
  }
}

cf-typegendeploy の上に移動し、 deploy の仲で cf-typegen を実行するようにしました。

cf-typegen というのは wrangler.jsonc からCloudflare Workers用の型定義ファイル worker-configuration.d.ts を生成するコマンドです。
そのためデプロイを行う際には、生成を行ってからデプロイを行うようにします。

1-3. wrangler.jsoncを修正する

wrangler.jsonc
{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "worker-subscribe-channel-messages",
  "main": "src/index.ts",
  "compatibility_date": "2026-08-13",
  "observability": {
    "enabled": true
  },
  "upload_source_maps": true,
  "workers_dev": true,
  "preview_urls": false
 }

workers_devpreview_urls を追記します。
"workers_dev": true としてプロダクション用のURLが発行されるようにします (devと付いていますがプロダクション用のURLになります)。
"previews_urls": false としてプレビュー用のURLは生成されないようにします。

1-4. デプロイする

$ npx wrangler login
$ npm run deploy
npm notice run worker-subscribe-channel-messages@0.0.0 deploy
npm notice run npm run cf-typegen && wrangler deploy
npm notice run worker-subscribe-channel-messages@0.0.0 cf-typegen
npm notice run wrangler types

 wrangler 4.122.0
────────────────────
Generating project types...

interface __BaseEnv_Env {
}
declare namespace Cloudflare {
        interface GlobalProps {
                mainModule: typeof import("./src/index");
        }
        interface Env extends __BaseEnv_Env {}
}
interface Env extends __BaseEnv_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.

 wrangler 4.122.0
────────────────────
Total Upload: 0.19 KiB / gzip: 0.16 KiB
Worker Startup Time: 4 ms
Uploaded worker-subscribe-channel-messages (1.29 sec)
Deployed worker-subscribe-channel-messages triggers (0.78 sec)
  https://worker-subscribe-channel-messages.luciferous.workers.dev
Current Version ID: b043f9e8-800f-4f5f-ab41-4a6fb7d3b8c8

npx wrangler login でwranglerコマンドでデプロイするために認証を通します。
ブラウザで認証を行ってください。

npm run deploy でデプロイを行います。

59981ebf-ed17-4859-8b96-d1da96e47005

Cloudflareのダッシュボードを見ると、デプロイされていることがわかります。
今回のURLは https://worker-subscribe-channel-messages.luciferous.workers.dev/ です。

2. Slack Appの初期設定を行う

Event Subscriptionに使うSlack Appを作成し、OAuthのパーミッションスコープなど初期設定を行います。

Event Subscriptionの設定は後で行います。
(Workerのコードなどを修正する必要があるので)

2-1. Slack Appを作成する

https://api.slack.com/apps にアクセスします。
(Slack AppをインストールするSlack Workspaceへの認証を通す必要がある場合があります)

54a89f30-4acd-4415-a900-9ea64502b716

Create New App をクリックします。

6036f5d0-fbce-4539-b3e8-fa69f7194ddc

Blank app を選択し、 Continue をクリックします。

f53fb812-4f11-4ce0-ad88-5b899892db6b

App name に任意の名前を入力し、インストールするワークスペースを Workspace で選択します。
ここでは worker-subscribe-channel-messages という名前にします。

b53c3733-e7ac-4dda-a6ef-9faaf817dd80

入力と選択をしたら Create をクリックします。

1b03c79a-2b44-4a95-83e0-901beb034195

Slack Appが作成されました。

2-2. ボット トークンのスコープを設定する

Features -> OAuth & Permissions を開き、 スコープ までスクロールします。

398033f0-4156-4f36-8bed-74b00d453b5a

今回はチャンネルにメッセージが投稿されたことをサブスクライブしたいので channels:history をボット トークンのスコープに追加します。

6d4726fd-7ce5-4854-9253-d414618e017e

上にスクロールすると、 OAuth TokensInstall to xxxx (xxxx はSlack Workspaceの名前)というボタンが有効になっています。
(ここでは Install to luciferous-workspace という名前になっています)

b55549b7-347f-4857-b4ee-66cf0777cf90

Install to luciferous-workspace をクリックします。

8de6ce1e-9d5d-410c-8ba4-db79c1157007

確認画面が表示されるので 許可する をクリックします。

6cefed76-35ff-4183-a1a8-2de7c333aa61

Slack Workspaceへのインストールが完了し、 Bot User OAuth Token が発行されています。
このトークンはまた後で使います。

2-3. Socket Modeをオフにする

今回はCloudflare WorkersのURLにリクエストを投げさせたいので Socket Modeはオフにする必要があります。

Settings -> Socket Mode を開きます。

f9f00f78-2f3d-4b04-8b3c-e44bfb194206

Enable Socket Mode のトグルがオン (緑) になっていたら、クリックしてオフにします。

f961b1f0-c10f-4ce3-ba84-1d3c0c89a270

Socket Modeがオフになりました。

3. WorkerのSecretを設定し、コードを更新する

Slack Appの準備ができたので、Secretを設定し、コードを実装します。

3-1. SLACK_BOT_USER_OAUTH_TOKEN をSecretに設定する

先ほどスコープを設定したときに生成されたトークンをSecretに設定します。

$ npx wrangler secret put SLACK_BOT_USER_OAUTH_TOKEN
npm notice run worker-subscribe-channel-messages@0.0.0 npx
npm notice run 'wrangler' secret put SLACK_BOT_USER_OAUTH_TOKEN

 wrangler 4.122.0
────────────────────
 Enter a secret value: **********************************************************
🌀 Creating the secret for the Worker "worker-subscribe-channel-messages"
 Success! Uploaded secret SLACK_BOT_USER_OAUTH_TOKEN

3-2. SLACK_SIGNING_SECRET をSecretに設定する

Slackからのリクエストか署名検証するのに使用するSigning Secretを Secretに設定します。

Settings -> Basic Information を開きます。

26c502d7-b174-4ecc-98bf-fb165b545f53

Signing SecretShow ボタンをクリックすると値が表示されます。
この値を先ほどと同様にSecretに保存します。

$ npx wrangler secret put SLACK_SIGNING_SECRET
npm notice run worker-subscribe-channel-messages@0.0.0 npx
npm notice run 'wrangler' secret put SLACK_SIGNING_SECRET

 wrangler 4.122.0
────────────────────
 Enter a secret value: ********************************
🌀 Creating the secret for the Worker "worker-subscribe-channel-messages"
 Success! Uploaded secret SLACK_SIGNING_SECRET

3-3. wrangler.jsoncに必要なSecretを列挙し、型定義ファイルを再生成する

wrangler.jsonc
{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "worker-subscribe-channel-messages",
  "main": "src/index.ts",
  "compatibility_date": "2026-08-13",
  "observability": {
    "enabled": true
  },
  "upload_source_maps": true,
  "workers_dev": true,
  "preview_urls": false,
  "secrets": {
    "required": ["SLACK_BOT_USER_OAUTH_TOKEN", "SLACK_SIGNING_SECRET"]
  }
}

末尾に secrets を追加します。

wrangler.jsoncに secrets.required があるとき、ここに列挙されたSecretがないとデプロイが失敗するようになります。
また、npm run cf-typegen をすると、 Env という型に列挙したSecretにアクセスするための型定義が追加されます。

$ npm run cf-typegen
worker-configuration.d.ts(一部抜粋)
interface __BaseEnv_Env {
  SLACK_BOT_USER_OAUTH_TOKEN: string;
  SLACK_SIGNING_SECRET: string;
}
declare namespace Cloudflare {
  interface GlobalProps {
    mainModule: typeof import("./src/index");
  }
  interface Env extends __BaseEnv_Env {}
}
interface Env extends __BaseEnv_Env {}

これで設定したSecretの型定義もできました。

3-4. index.tsを実装する

最初に slack-edge をインストールします。

$ npm install slack-edge

次にWorker用のコードを修正します。

src/index.ts
import { SlackApp, SlackEdgeAppEnv } from "slack-edge";

export default {
  async fetch(request, env: Env, ctx): Promise<Response> {
    const slackEnv: SlackEdgeAppEnv = {
      SLACK_SIGNING_SECRET: env.SLACK_SIGNING_SECRET,
      SLACK_BOT_TOKEN: env.SLACK_BOT_USER_OAUTH_TOKEN,
      SLACK_LOGGING_LEVEL: "DEBUG",
    };

    const app = new SlackApp({
      env: slackEnv,
    }).event("message", async ({ payload }) => {
      console.debug({ message: "message event payload", payload });
    });

    return await app.run(request, ctx);
  },
} satisfies ExportedHandler<Env>;

コードを修正したらデプロイします。

$ npm run deploy
npm notice run worker-subscribe-channel-messages@0.0.0 deploy
npm notice run npm run cf-typegen && wrangler deploy
npm notice run worker-subscribe-channel-messages@0.0.0 cf-typegen
npm notice run wrangler types

 wrangler 4.122.0
────────────────────
Generating project types...

interface __BaseEnv_Env {
        SLACK_BOT_USER_OAUTH_TOKEN: string;
        SLACK_SIGNING_SECRET: string;
}
declare namespace Cloudflare {
        interface GlobalProps {
                mainModule: typeof import("./src/index");
        }
        interface Env extends __BaseEnv_Env {}
}
interface Env extends __BaseEnv_Env {}
type StringifyValues<EnvType extends Record<string, unknown>> = {
        [Binding in keyof EnvType]: EnvType[Binding] extends string ? EnvType[Binding] : string;
};
declare namespace NodeJS {
        interface ProcessEnv extends StringifyValues<Pick<Cloudflare.Env, "SLACK_BOT_USER_OAUTH_TOKEN" | "SLACK_SIGNING_SECRET">> {}
}

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.

 wrangler 4.122.0
────────────────────
Total Upload: 178.51 KiB / gzip: 27.67 KiB
Worker Startup Time: 7 ms
Uploaded worker-subscribe-channel-messages (2.57 sec)
Deployed worker-subscribe-channel-messages triggers (0.95 sec)
  https://worker-subscribe-channel-messages.luciferous.workers.dev
Current Version ID: e8dea3cf-38d8-408a-8569-a3fe426fb955

3-5. 備考: コードについて

slack-edge のREADMEに記載されているサンプルコードは次のようになっています。

import { SlackApp, SlackEdgeAppEnv } from "slack-cloudflare-workers";

export default {
  async fetch(
    request: Request,
    env: SlackEdgeAppEnv,
    ctx: ExecutionContext
  ): Promise<Response> {
    const app = new SlackApp({ env })
      .command("/hello-cf-workers",
        async (req) => {
          // sync handler, which is resposible to ack the request
          return ":wave: This app runs on Cloudflare Workers!";
          // If you don't have anything to do here, the function doesn't need to return anything
          // This means in that case, simply having `async () => {}` works for you
        },
        async ({ context: { respond } }) => {
          // Lazy listener, which can be executed asynchronously
          // You can do whatever may take longer than 3 seconds here
          await respond({ text: "This is an async reply. How are you doing?" });
        }
      );
    return await app.run(request, ctx);
  },
};

サンプルコードでは slack-edge で定義された SlackEdgeAppEnv をenvの型として使用しています。

SlackEdgeAppEnvで定義されている型に従ってSecretを設定しているときにはこちらのコードで構いません。
しかし、この書き方では npm run cf-typegen によるEnvの自動生成の恩恵を受けることができません。
そのため、自分の作成したコードでは自動生成したEnvをenvの型として扱い、 SlackAppクラスのPropsのenv に渡すようにしています。

4. Slack AppにEvent Subscriptionsの設定を行う

Workerの準備ができたのでSlack AppにEvent Subscriptionの設定を行います。

4-1. Event SubscriptionsにURLを設定し、サブスクライブするイベントを指定する

Features -> Event Subscriptions を開きます。

7df96b49-ad26-4d78-9580-ac85f11b37bf

Enable Events のトグルボタンをクリックし、Onにします。

90dccbb1-f473-404a-9a58-34859bada7be

Request URL にWorkerのURLを入力します。

16456fa9-1b3e-4647-acce-235ebf9f668f

WorkerのコードとSecretを設定しているので Verified となります。

Subscribe to bot events をクリックします。

6d63d9c0-bbb6-4f3d-bd6e-4aa1e8c84df8

Add Bot User Event をクリックし、 message.channels を追加します。

d3a0bf7f-3118-4787-b7bc-b9954c9d992f

Save Changes をクリックして反映します。
これでEvent Subscriptionsの準備ができました。

Workerのログを確認すると、Verifyのリクエストが来たこともわかります。

15a3a4e3-72a5-4f5c-9ecc-1e1fab0bb70a

5. 動かしてみる

Slack Workspaceのチャンネルに追加して、投稿されたメッセージがサブスクライブできるか試します。

5-1. チャンネルにSlack Appを追加する

604b9a14-b781-4b6b-ab2b-5fecf893add6

チャンネルにSlack App追加する方法はいくつかありますが、今回はメンションを飛ばす方法を使います。
追加する をクリックし、追加します。

ef332155-4ad0-4766-8d29-de212de5fde2

5-2. チャンネルにメッセージを投稿して、Workerでサブスクライブできているか確認する

da133f1d-45d7-40e2-8240-44bdb424df2d

メッセージを投稿したので、Workerのログを確認します。

63bb9ae2-1db6-476b-a4ed-df3e923007f7

きちんとサブスクライブできていることがわかりました。

まとめ

以上、Cloudflare WorkersでSlack Appのバックエンドを動かす方法でした。

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

引用

この記事をシェアする

関連記事