Skip to content

Cloudflare Workers

Hono is designed (primarily) for Cloudflare Workers, while Honocord works well with handling async interactions automatically to fit within Workers’ limits.

  • Global edge deployment with low latency.
  • Generous free tier (100k requests/day).
  • No cold starts, instant scaling.
  • Honocord auto-manages async via waitUntil.

See a full example here

  1. Initialize

    Terminal window
    pnpm create cloudflare@latest my-discord-bot
    cd my-discord-bot
    pnpm add honocord
  2. wrangler.jsonc

    {
    "name": "my-discord-bot",
    "main": "src/index.ts",
    "compatibility_date": "2024-01-01",
    "compatibility_flags": ["nodejs_compat"],
    "vars": {
    "IS_CF_WORKER": "true",
    },
    }
  3. Bot Entry Point

    src/index.ts
    import { Honocord } from "honocord";
    import * as handlers from "./handlers";
    const bot = new Honocord({ isCFWorker: true }); // Auto-detected via IS_CF_WORKER
    bot.loadHandlers(...Object.values(handlers));
    export default bot.getApp(); // Export as Worker fetch handler
  4. Example Handler

    src/handlers.ts
    import { SlashCommandHandler } from "honocord";
    export const pingCommand = new SlashCommandHandler()
    .setName("ping")
    .setDescription("Check bot latency")
    .addHandler(async (interaction) => {
    await interaction.reply("🏓 Pong!");
    });

    Never use plain environment variables for sensitive data.

    Terminal window
    npx wrangler secret put DISCORD_TOKEN
    npx wrangler secret put DISCORD_PUBLIC_KEY
    npx wrangler secret put DISCORD_APPLICATION_ID

    Or have an .env file and bulk upload your secrets with:

    Terminal window
    npx wrangler secret bulk .env
    Terminal window
    wrangler dev # Runs at http://localhost:8787

    To expose your local environment publicly for Discord’s verification:

    Terminal window
    cloudflared tunnel --url http://localhost:8787 # Recommended
    # or
    ngrok http 8787
  1. Deploy

    Terminal window
    pnpm run deploy # Outputs your Worker URL, e.g., https://my-discord-bot.username.workers.dev/
  2. Register Commands

    Run this locally, not on the Worker itself.

    src/register.ts
    import { registerCommands } from "honocord";
    import * as handlers from "./handlers/index"; // Assuming you have a src/handlers/index.ts file
    await registerCommands(process.env.DISCORD_TOKEN!, process.env.DISCORD_APPLICATION_ID!, Object.values(handlers));
    package.json
    {
    "scripts": {
    "register": "tsx --env-file=.env src/register.ts"
    },
    "devDependencies": {
    "tsx": "latest"
    }
    }

    Then run with pnpm register.

  3. Discord Setup

    1. In the Discord Developer Portal → Your App → General Information.
    2. Set Interactions Endpoint URL to your Worker URL (e.g., https://.../interactions).
    3. Save changes. Discord will automatically send a POST request to verify the URL.

When you deploy on Workers, you should use the wrangler script cf-typegen automatically provided upon project creation.

This creates a worker-configuration.d.ts file which exports an Env interface which you can use in your project.

src/types.ts
import type { BaseInteractionContext } from "honocord";
// `Env` is from the types generated by wrangler
type WorkerEnv = Env;
export type MyVariables = {
// Your custom Hono variables here
};
type MyContext = BaseInteractionContext<WorkerEnv, MyVariables>;

Usage in a handler:

import type { Context } from "./types";
// ... inside handler ...
await interaction.context.env.MY_KV.get("key");
// Note: Don't confuse .context (the Hono Context) with .contexts (Discord Interaction Context)

See a full example here

  • Use secrets only for sensitive data.
  • Keep handlers lightweight (≤50ms CPU time on the free plan).
  • Store data in Workers KV, D1, or Durable Objects.
  • Monitor logs with wrangler tail.
  • Update compatibility_date on a regular basis.
  • CPU limit exceeded: Split heavy work or use Durable Objects. Alternatively, upgrade to the Workers Paid plan.
  • Verification fails: Double-check your public key, ensure the URL is correct (including any path like /interactions), and ensure the Worker is deployed.
  • Module not found: Ensure you are using the correct relative paths and file extensions in your imports.