{"id":24922322,"url":"https://github.com/slack-edge/slack-edge","last_synced_at":"2025-05-16T12:04:42.999Z","repository":{"id":160310481,"uuid":"635197518","full_name":"slack-edge/slack-edge","owner":"slack-edge","description":"Slack app development framework for edge functions with streamlined TypeScript support","archived":false,"fork":false,"pushed_at":"2025-04-11T06:30:49.000Z","size":626,"stargazers_count":120,"open_issues_count":3,"forks_count":11,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-05-08T20:49:37.822Z","etag":null,"topics":["cloudflare-workers","fetch-api","slack","slack-api","slack-bot","typescript","typescript-library","vercel-edge-functions"],"latest_commit_sha":null,"homepage":"https://github.com/seratch/slack-edge-app-template","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/slack-edge.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-05-02T07:13:43.000Z","updated_at":"2025-05-06T09:52:47.000Z","dependencies_parsed_at":"2024-01-12T03:34:05.987Z","dependency_job_id":"92f820f8-e424-40ef-bc30-2f0226f32f06","html_url":"https://github.com/slack-edge/slack-edge","commit_stats":null,"previous_names":["slack-edge/slack-edge"],"tags_count":67,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slack-edge%2Fslack-edge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slack-edge%2Fslack-edge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slack-edge%2Fslack-edge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slack-edge%2Fslack-edge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/slack-edge","download_url":"https://codeload.github.com/slack-edge/slack-edge/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253166474,"owners_count":21864472,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cloudflare-workers","fetch-api","slack","slack-api","slack-bot","typescript","typescript-library","vercel-edge-functions"],"created_at":"2025-02-02T11:06:56.629Z","updated_at":"2025-05-16T12:04:42.954Z","avatar_url":"https://github.com/slack-edge.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Slack Edge\n\n[![npm version](https://badge.fury.io/js/slack-edge.svg)](https://badge.fury.io/js/slack-edge) \n[![deno module](https://shield.deno.dev/x/slack_edge)](https://deno.land/x/slack_edge)\n\nThe **slack-edge** library is a Slack app development framework designed specifically for the following runtimes:\n\n* [Cloudflare Workers](https://workers.cloudflare.com/)\n* [Vercel Edge Functions](https://vercel.com/docs/concepts/functions/edge-functions/quickstart)\n* [Supabase Edge Functions](https://supabase.com/docs/guides/functions)\n\nNot only does it work with the above, but it also functions with the latest versions of [Deno](https://deno.com/), [Bun](https://bun.sh/), and [Node.js](https://nodejs.org/en/about).\n\nThis framework draws significant inspiration from Slack's [Bolt framework](https://api.slack.com/tools/bolt), but its design does not strictly follow the [bolt-js](https://github.com/slackapi/bolt-js) blueprint. Key differences include:\n\n* **Edge function ready**: Out-of-the-box edge function (e.g., Cloudflare Workers) support\n* **TypeScript focused**: Enhances type safety and clarifies typings for developers\n* **Lazy listener enabled**: [bolt-python's lazy listener feature](https://tools.slack.dev/bolt-python/concepts/lazy-listeners) is provided out of the box\n* **Zero additional dependencies**: No other dependencies required beyond TypeScript types and [slack-web-api-client](https://github.com/slack-edge/slack-web-api-client) (our fetch-function-based Slack API client)\n\n## Getting Started\n\nCurrently, you can use this library for three different platforms.\n\n### Cloudflare Workers\n\n[Cloudflare Workers](https://workers.cloudflare.com/) is an edge function platform for quickly deploying an app.\n\nThe only additional thing you need to do is to add [the **slack-cloudflare-workers** package](https://github.com/slack-edge/slack-cloudflare-workers) to your Cloudflare Workers app project. The **slack-cloudflare-workers** package enhances **slack-edge** with Cloudflare Workers-specific features, such as KV-based installation stores.\n\n```bash\nnpm install -g wrangler@latest\nnpx wrangler generate my-slack-app\ncd my-slack-app\nnpm i slack-cloudflare-workers@latest\n```\n\nA simple app that handles a slash command can be structured like this:\n\n```typescript\nimport { SlackApp, SlackEdgeAppEnv } from \"slack-cloudflare-workers\";\n\nexport default {\n  async fetch(\n    request: Request,\n    env: SlackEdgeAppEnv,\n    ctx: ExecutionContext\n  ): Promise\u003cResponse\u003e {\n    const app = new SlackApp({ env })\n      .command(\"/hello-cf-workers\",\n        async (req) =\u003e {\n          // sync handler, which is resposible to ack the request\n          return \":wave: This app runs on Cloudflare Workers!\";\n          // If you don't have anything to do here, the function doesn't need to return anything\n          // This means in that case, simply having `async () =\u003e {}` works for you\n        },\n        async ({ context: { respond } }) =\u003e {\n          // Lazy listener, which can be executed asynchronously\n          // You can do whatever may take longer than 3 seconds here\n          await respond({ text: \"This is an async reply. How are you doing?\" });\n        }\n      );\n    return await app.run(request, ctx);\n  },\n};\n```\n\nAlso, before running this app, don't forget to create a `.dev.vars` file with your env variables for local development:\n\n```\nSLACK_SIGNING_SECRET=....\nSLACK_BOT_TOKEN=xoxb-...\nSLACK_LOGGING_LEVEL=DEBUG\n```\n\nWhen you run `wrangler dev --port 3000`, your app process spins up, and it starts handling API requests at `http://localhost:3000/slack/events`. You may want to run Cloudflare Tunnel or something equivalent to forward public endpoint requests to this local app process.\n\nRefer to https://github.com/slack-edge/slack-cloudflare-workers/blob/main/docs/index.md for further guidance.\n\n### Vercel Edge Functions\n\n[Vercel Edge Functions](https://vercel.com/docs/functions/edge-functions) is an edge function platform, which is part of [Vercel](https://vercel.com/)'s Frontend Cloud.\n\nCreate a new Next.js project w/ slack-edge library by following these steps:\n\n```bash\nnpx create-next-app@latest --typescript\ncd your-app-name\nnpm i slack-edge\n```\n\nCreate a new source file `app/api/slack.ts` that contains the following code:\n\n```typescript\nimport type { NextRequest } from \"next/server\";\nimport { waitUntil } from '@vercel/functions';\nimport { SlackApp } from \"slack-edge\";\n\nexport const config = {\n  runtime: \"edge\",\n};\n\nconst app = new SlackApp({\n  env: {\n    SLACK_SIGNING_SECRET: process.env.SLACK_SIGNING_SECRET!,\n    SLACK_BOT_TOKEN: process.env.SLACK_BOT_TOKEN!,\n    SLACK_LOGGING_LEVEL: \"DEBUG\",\n  },\n});\n\napp.command(\"/hello-edge\",\n  async (req) =\u003e {\n    // sync handler, which is resposible to ack the request\n    return \":wave: This app runs on Vercel Edge Function platform!\";\n    // If you don't have anything to do here, the function doesn't need to return anything\n    // This means in that case, simply having `async () =\u003e {}` works for you\n  },\n  async ({ context: { respond } }) =\u003e {\n    // Lazy listener, which can be executed asynchronously\n    // You can do whatever may take longer than 3 seconds here\n    await respond({ text: \"This is an async reply. How are you doing?\" });\n  }\n);\n\nexport async function POST(req: NextRequest) {\n  return await app.run(req, { waitUntil });\n}\n```\n\nWith this code, your **Request URL** for handling requests from Slack API server will be `https://{your public domain}/api/slack` instead of `/slack/events`.\n\nLastly, you can run the app by following these steps:\n\n```bash\nexport SLACK_SIGNING_SECRET=\"...\"\nexport SLACK_BOT_TOKEN=\"xoxb-...\"\nnpm run dev\n```\n\nIf you use Cloudflare Tunnel or something equivalent, you may want to run `cloudflared tunnel --url http://localhost:3000` in a different terminal window.\n\nWe are currently working on the **slack-vercel-edge-functions** package, which offers Vercel-specific features in addition to the core ones provided by **slack-edge**. Until its release, please use slack-edge directly and implement the missing features on your own as necessary.\n\n### Supabase Edge Functions\n\n[Supabase](https://supabase.com/) is a BaaS that provides a full PostgreSQL database, along with other products such as [Supabase Edge Functions](https://supabase.com/docs/guides/functions).\n\nCreate a new Supabase project locally by following these steps:\n\n```bash\nmkdir myapp\ncd myapp\n\nbrew install supabase/tap/supabase\nsupabase init\nsupabase functions new hello-world\n```\n\nOpen the source file `supabase/functions/hello-world/index.ts` and replace it with the following:\n\n```typescript\nimport {\n  SlackApp,\n  SlackEdgeAppEnv,\n} from \"https://deno.land/x/slack_edge@1.3.7/mod.ts\";\n\nconst app = new SlackApp\u003cSlackEdgeAppEnv\u003e({\n  env: {\n    SLACK_SIGNING_SECRET: Deno.env.get(\"SLACK_SIGNING_SECRET\")!,\n    SLACK_BOT_TOKEN: Deno.env.get(\"SLACK_BOT_TOKEN\"),\n    SLACK_LOGGING_LEVEL: \"DEBUG\",\n  },\n});\n\napp.command(\"/hello-edge\", async (req) =\u003e {\n  // sync handler, which is resposible to ack the request\n  return \":wave: This app runs on Vercel Edge Function platform!\";\n  // If you don't have anything to do here, the function doesn't need to return anything\n  // This means in that case, simply having `async () =\u003e {}` works for you\n}, async ({ context: { respond } }) =\u003e {\n  // Lazy listener, which can be executed asynchronously\n  // You can do whatever may take longer than 3 seconds here\n  await respond({ text: \"This is an async reply. How are you doing?\" });\n});\n\nDeno.serve(async (req) =\u003e {\n  return await app.run(req);\n});\n```\n\n#### Run locally\n\nCreate a `supabase/functions/.env` file with the following:\n\n```sh\nexport SLACK_SIGNING_SECRET=\"...\"\nexport SLACK_BOT_TOKEN=\"xoxb-...\"\n```\n\nThen run:\n\n```sh\n# Terminal A\nsupabase start\nsupabase functions serve --env-file supabase/functions/.env\n\n# Terminal B\nbrew install cloudflare/cloudflare/cloudflared\ncloudflared tunnel --url http://127.0.0.1:54321\n```\n\n#### Run in the cloud\n\nIn a browser, login to your Supabase account and create a new project.\n\nThen open a terminal in your `myapp`'s project directory and run:\n\n```sh\nsupabase login\n```\n\nLink the Supabase project you just made:\n\n```sh\nsupabase link\n```\n\nDeploy your `hello-world` edge function.\n\n```sh\nsupabase functions deploy hello-world --no-verify-jwt\n```\n\nFinally, add your environment variables:\n\n* Go to your Supabase project\n* Go to the Edge Functions tab, and click on your edge functions\n* Tap on Manage Secrets and then add your `SLACK_BOT_TOKEN` and `SLACK_SIGNING_SECRET`\n\n### Run Your App with Deno / Bun / Node.js\n\nThis library is available not only for edge function use cases but also for any JavaScript runtime use cases. Specifically, you can run an app with [Deno](https://deno.com/), [Bun](https://bun.sh/), and [Node.js](https://nodejs.org/en/about). To learn more about this, please check the example files under the `./test` directory.\n\n#### Run with Bun\n\n```typescript\nimport { SlackApp } from \"slack-edge\";\n\nconst app = new SlackApp({\n  env: {\n    SLACK_SIGNING_SECRET: process.env.SLACK_SIGNING_SECRET!,\n    SLACK_BOT_TOKEN: process.env.SLACK_BOT_TOKEN,\n    SLACK_LOGGING_LEVEL: \"DEBUG\",\n  },\n});\n\n// Add listeners here\n\nexport default {\n  port: 3000,\n  async fetch(request) {\n    return await app.run(request);\n  },\n};\n```\n\nYou can run the app by:\n\n```bash\n# Terminal A\nbun run --watch my-app.ts\n# Terminal B\nbrew install cloudflare/cloudflare/cloudflared\ncloudflared tunnel --url http://localhost:3000\n```\n\n#### Run with Deno\n\nPlease refer to [README for the Deno module](https://github.com/slack-edge/slack-edge/blob/main/src_deno/README.md).\n\n#### Run with Node.js (Socket Mode: Production-ready)\n\nIf you need a stable Socket Mode integration, we recommend using `@slack/socket-mode` along with this package. With Node 20+, the following example works for you:\n\n```typescript\n// npm i slack-edge @slack/socket-mode\nimport {\n  SlackApp,\n  fromSocketModeToRequest,\n  fromResponseToSocketModePayload,\n} from \"slack-edge\";\nimport { SocketModeClient } from \"@slack/socket-mode\";\nimport { LogLevel } from \"@slack/logger\";\n\nconst app = new SlackApp({\n  socketMode: true,\n  env: {\n    SLACK_BOT_TOKEN: process.env.SLACK_BOT_TOKEN!,\n    SLACK_APP_TOKEN: process.env.SLACK_APP_TOKEN!,\n    SLACK_LOGGING_LEVEL: \"DEBUG\",\n  },\n});\n\n// Add listeners here\napp.command(\"/hello\", async ({}) =\u003e {\n  return \"Hi!\";\n});\n\n// Start a Socket Mode client\nconst socketModeClient = new SocketModeClient({\n  appToken: process.env.SLACK_APP_TOKEN!,\n  logLevel: LogLevel.DEBUG,\n});\nsocketModeClient.on(\"slack_event\", async ({ body, ack, retry_num: retryNum, retry_reason: retryReason }) =\u003e {\n  const request = fromSocketModeToRequest({ body, retryNum, retryReason });\n  if (!request) { return; }\n  const response = await app.run(request);\n  await ack(await fromResponseToSocketModePayload({ response }));\n},);\n(async () =\u003e {\n  await socketModeClient.start();\n})();\n```\n\nIf you want to build a Slack app this way, [this project template](https://github.com/slack-edge/slack-edge-app-template) should be pretty useful for you!\n\n#### Run with Remix + Node.js\n\nIf you're looking for a way to serve Slack app using [Remix](https://remix.run/), slack-edge is the best way to go! Here is a simple example to run your Slack app as part of a Remix app:\n\n```typescript\nimport { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { SlackApp } from \"slack-edge\";\n\nexport async function action({ request }: LoaderFunctionArgs) {\n  const app = new SlackApp({\n    env: {\n      SLACK_BOT_TOKEN: process.env.SLACK_BOT_TOKEN,\n      SLACK_SIGNING_SECRET: process.env.SLACK_SIGNING_SECRET!,\n    }\n  });\n  // Add listeners here\n\n  return await app.run(request);\n}\n```\n\nYou can run the app by following these steps:\n* `npx create-remix@latest`\n* `cd my-remix-app`\n* `npm i @remix-run/serve @remix-run/node slack-edge`\n* Add `app/routes/api.slack.tsx` (if you go with `POST /api/slack`) with the above code\n* `export SLACK_SIGNING_SECRET=...`\n* `export SLACK_BOT_TOKEN=xoxb-...`\n* `npm run dev`\n\n### Reference\n\n#### Middleware\n\nThis framework offers ways to globally customize your app's behavior, like you do when developing web apps. A common example is to attach extra data to the `context` object for following listeners.\n\n|Pattern|Description|\n|-|-|\n|app.beforeAuthorize|The passed function does something before calling `authorize()` function. If this method returns `SlackResponse`, the following middleware and listeners won't be executed.|\n|app.afterAuthorize / app.use / app.middleware|The passed function does something right after calling `authorize()` function. If this method returns `SlackResponse`, the following middleware and listeners won't be executed.|\n\n#### `ack` / `lazy` Functions\n\nYou may be unfamiliar with the \"lazy listener\" concept in this framework. To learn more about it, please read bolt-python's documentation: https://tools.slack.dev/bolt-python/concepts/lazy-listeners\n\nThe `ack` function must complete within 3 seconds, while the `lazy` function can perform time-consuming tasks. It's important to note that not all request handlers support the ack or lazy functions. For more information, please refer to the following table, which covers all the patterns in detail.\n\nStarting from v0.9, if desired, you can execute lazy listeners after the completion of their ack function. To customize the behavior in this manner, you can pass `startLazyListenerAfterAck: true` as one of the arguments in the `App` constructor.\n\n|Pattern|Description|`ack` function|`lazy` function|\n|-|-|-|-|\n|app.assistant|The passed collection of functions handles an assistant thread request pattern. The feature needs to be turned on in advance.|x|◯|\n|app.command|The passed function handles a slash command request pattern. If the function returns a message, the message will be posted in the channel where the end-user invoked the command.|◯|◯|\n|app.event|The passed function asynchronously does something when an Events API request that matches the constraints comes in. Please note that manually acknowledge a request is unsupported. You can pass only one function, which can be executed as a lazy listener.|x|◯|\n|app.function|The passed function asynchronously does something when a \"function_executed\" event request that matches the constraints comes in. Please note that manually acknowledge a request is unsupported. You can pass only one function, which can be executed as a lazy listener.|x|◯|\n|app.message / app.anyMessage|The passed function asynchronously does something when an a message event comes in. Please note that manually acknowledge a request is unsupported. You can pass only one function, which can be executed as a lazy listener. If the message pattern argument can be any of string, regexp, and undefined. When you pass undefined, the listener matches all messages.|x|◯|\n|app.shortcut / app.globalShortcut / app.messageShortcut|The passed function handles a global/message shortcut request pattern. Please note that returning a message text in the `ack` function does not work for shortcuts. Instead, you can use `context.respond` for it.|◯|◯|\n|app.action|The passed function handles a user interaction on a Block Kit component such as button clicks, item selection in a select menu, and so on.|◯|◯|\n|app.options|The passed function handles an external data source reqeust for Block Kit select menus. You cannnot respond to this request pattern asynchronously, so slack-edge enables developers to pass only `ack` function, which must complete within 3 seconds, here.|◯|x|\n|app.view / app.viewSubmission / app.viewClosed|The passed function handles either a modal data submission or the \"Close\" button click event. `ack` function can return various `response_action`s (errors, update, push, clear) and their associated data. If you want to simply close the modal, you don't need to return anything.|◯|◯|\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslack-edge%2Fslack-edge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fslack-edge%2Fslack-edge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslack-edge%2Fslack-edge/lists"}