Package Exports
- hook-engine
Readme
π hook-engine
Production-grade webhook engine with:
- β Signature verification (Stripe-style)
- β Retry + exponential backoff
- β Deduping (SQLite)
- β Local replay CLI
- β JSON event logs
- β Safe for serverless & edge runtimes
π Why hook-engine?
Stripe (and others) send webhooks β but most apps:
- π₯ Fail silently in serverless
- π Have no retry engine
- β οΈ Lack idempotency logic
- π Canβt replay/test webhooks locally
- π΅ Lose trace of webhook history
hook-engine
solves all that with:
π οΈ Drop-in devtool-grade library and CLI for real-world webhook infra.
π¦ Install
pnpm add hook-engine
Or globally for CLI usage:
pnpm add -g hook-engine
π Project Structure (Example)
π your-app/
β
ββ π¦ db/
β ββ seen.sqlite # Event ID deduping
β
ββ π lib/
β ββ config.ts # Webhook secret & source
β ββ handler.ts # Your business logic
β ββ dedupe.ts # Event deduplication store
β
ββ π pages/
β ββ api/
β ββ webhooks/
β ββ stripe.ts # Webhook entrypoint
π§ Usage (Next.js or Express)
// pages/api/webhooks/stripe.ts
import type { NextApiRequest, NextApiResponse } from "next";
import { receiveWebhook, retry } from "hook-engine";
import { config } from "../../../lib/config";
import { processEvent } from "../../../lib/handler";
import { isDuplicate, markSeen } from "../../../lib/dedupe";
export const configRuntime = {
api: {
bodyParser: false, // Required for raw body access
},
};
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
const event = await receiveWebhook(req, config);
if (isDuplicate(event.id)) {
console.warn(`β οΈ Duplicate skipped: ${event.id}`);
return res.status(200).send("duplicate");
}
markSeen(event.id);
await retry(event, processEvent);
res.status(200).send("ok");
} catch (err: any) {
console.error("β Webhook error:", err.message);
res.status(400).send("webhook error");
}
}
π§ Your Business Logic
// lib/handler.ts
export async function processEvent(event: any) {
console.log(`π― Received ${event.type} for ${event.id}`);
if (event.type === "invoice.paid") {
const customer = event.payload.customer;
console.log(`π Grant premium access to ${customer}`);
// do something: DB, email, billing...
}
if (event.type === "invoice.failed") {
throw new Error("π₯ Simulated failure for retry");
}
}
π§± Deduping Store (SQLite)
// lib/dedupe.ts
import Database from "better-sqlite3";
import path from "path";
import fs from "fs";
const DB_PATH = path.resolve(process.cwd(), "db/seen.sqlite");
fs.mkdirSync(path.dirname(DB_PATH), { recursive: true });
const db = new Database(DB_PATH);
db.exec(`CREATE TABLE IF NOT EXISTS seen_events (
id TEXT PRIMARY KEY,
seen_at INTEGER
);`);
export function isDuplicate(id: string): boolean {
return !!db.prepare("SELECT 1 FROM seen_events WHERE id = ?").get(id);
}
export function markSeen(id: string) {
db.prepare("INSERT OR IGNORE INTO seen_events (id, seen_at) VALUES (?, ?)").run(id, Date.now());
}
π Config Example
// lib/config.ts
import { WebhookConfig } from "hook-engine";
export const config: WebhookConfig = {
source: "stripe",
secret: process.env.STRIPE_WEBHOOK_SECRET!,
};
π» CLI Commands
# Replay from file
webhook-gateway replay ./mock/invoice-paid.json \
--target http://localhost:3000/api/webhooks/stripe \
--secret $STRIPE_WEBHOOK_SECRET
# View logged events
webhook-gateway logs
# JSON mode
webhook-gateway logs --json
# Replay by ID
webhook-gateway logs --replay evt_123456 \
--target http://localhost:3000/api/webhooks/stripe \
--secret $STRIPE_WEBHOOK_SECRET
All events are logged into
.webhook-engine/events.log
in newline-delimited JSON.
π‘ Dev Workflow
Start your server:
pnpm dev
In another terminal:
stripe listen --forward-to localhost:3000/api/webhooks/stripe
Trigger an event from Stripe Dashboard or CLI
Replay failed events like a boss:
webhook-gateway logs --replay evt_123 --target http://localhost:3000/api/webhooks/stripe
π§ͺ Supported Providers
- β Stripe (first-class)
- π‘ GitHub (soon)
- π‘ Clerk/Auth0 (soon)
- π’ Any JSON webhook provider (via adapters)
π‘οΈ Features
Feature | Status |
---|---|
Signature verification | β |
Retry engine | β |
Deduplication store | β |
Local replay CLI | β |
JSONL event logs | β |
SQLite support | β |
Serverless safe | β |
Extendable adapters | π οΈ coming |
π§ͺ Example Repos
Coming soon...
π License
MIT β by Naol Ketema
Star this repo if you love solid webhook tooling π Tweet it if it saved your weekend π