hooksentinel
Frameworks

Hono / Cloudflare Workers

Handle webhooks with hooksentinel on Hono, including Cloudflare Workers where Node's crypto module isn't available.

Install

npm install @hooksentinel/core

@hooksentinel/core/hono exports toHonoHandler. The core package uses Web Crypto (SubtleCrypto) rather than Node's crypto module, so it runs unmodified on Cloudflare Workers, Deno Deploy, and other edge/isolate runtimes — there is no Node-only code path to swap out.

Basic setup

src/index.ts
import { Hono } from 'hono';
import { createWebhookHandler, stripe } from '@hooksentinel/core';
import { toHonoHandler } from '@hooksentinel/core/hono';
import { memoryStore } from '@hooksentinel/core/stores';

const app = new Hono();

const stripeWebhook = createWebhookHandler({
  provider: stripe({ secret: '' }), // see env binding below
  idempotency: memoryStore(),
  onEvent: async (event) => {
    if (event.type === 'checkout.session.completed') {
      await fulfillOrder(event.data.object.id);
    }
  },
});

app.post('/webhooks/stripe', toHonoHandler(stripeWebhook));

async function fulfillOrder(sessionId: string) {
  // ...
}

export default app;

Cloudflare Workers: reading secrets from bindings

Workers don't have process.env — secrets come from the Env bindings object passed into the request. Build the handler per-request instead of at module scope so it can read the binding:

src/index.ts
import { Hono } from 'hono';
import { createWebhookHandler, stripe } from '@hooksentinel/core';
import { toHonoHandler } from '@hooksentinel/core/hono';
import { memoryStore } from '@hooksentinel/core/stores';

type Bindings = {
  STRIPE_WEBHOOK_SECRET: string;
};

const app = new Hono<{ Bindings: Bindings }>();

app.post('/webhooks/stripe', async (c) => {
  const stripeWebhook = createWebhookHandler({
    provider: stripe({ secret: c.env.STRIPE_WEBHOOK_SECRET }),
    idempotency: memoryStore(),
    onEvent: async (event) => {
      if (event.type === 'checkout.session.completed') {
        await fulfillOrder(event.data.object.id);
      }
    },
  });

  return toHonoHandler(stripeWebhook)(c);
});

async function fulfillOrder(sessionId: string) {
  // ...
}

export default app;

Idempotency on Workers

memoryStore() doesn't survive across isolate invocations on Workers — each request may hit a cold or different isolate, so in-memory dedup is not reliable there. Use redisStore() pointed at Upstash Redis (which speaks HTTP and works from Workers) or a KV-backed store implementing the same IdempotencyStore interface. See Idempotency for the interface and setup.

Deno

The same toHonoHandler works unmodified under Deno's Hono support, since both the core package and the Hono adapter avoid Node built-ins.

Last updated on

On this page