A production-ready webhook testing and debugging tool built with Cloudflare Durable Objects, demonstrating the power of edge computing with persistent SQLite storage and real-time WebSocket updates.
Perfect for: Testing webhooks from third-party services, debugging API integrations, monitoring webhook payloads, and learning Cloudflare Durable Objects.
- π― Instant Webhook URLs: Generate unique webhook endpoints on-demand with zero configuration
- π‘ Real-time Updates: See incoming requests instantly via WebSocket connections
- πΎ Persistent Storage: All requests stored in SQLite (up to 100 most recent per webhook)
- π Complete Request Details: Captures method, headers, body, query parameters, and timestamps
- π Cloudflare Metadata: Automatic geolocation and network information for each request
- π¨ Modern UI: Clean, responsive interface built with Tailwind CSS and Alpine.js
- π¨ Syntax Highlighting: Beautiful code formatting with Shiki for JSON, XML, HTML, and more
- β‘ Edge Computing: Runs on Cloudflare's global network for ultra-low latency worldwide
- π Zero Database Setup: SQLite is built into Durable Objectsβno external database needed
Visit the live demo: webhookflare.fayaz.workers.dev
- API Development: Test webhooks during local development without exposing localhost
- Integration Testing: Verify webhook payloads from services like Stripe, GitHub, Shopify, etc.
- Debugging: Inspect exactly what data third-party services are sending
- Monitoring: Track webhook reliability and response times
- Education: Learn how Durable Objects, SQLite, and WebSockets work together
WebhookFlare showcases several Cloudflare Workers platform features:
Each webhook endpoint is a separate Durable Object instance that:
- Automatically provisions a SQLite database on first access
- Stores up to 100 most recent requests with full details
- Maintains strong consistency within a single object
- Persists data across restarts and deploys
- Uses RPC methods (
captureRequest,getRequests) for communication
Real-time updates powered by:
- Native WebSocket support in Durable Objects
- Connection management across multiple viewers
- Instant broadcast of new requests to all connected clients
- Automatic reconnection handling
The main Worker handles:
- Routing to Durable Object instances by webhook ID
- Serving static assets (HTML, CSS, JS)
- CORS headers for cross-origin requests
- Cloudflare metadata extraction (
request.cfobject)
Single-page application using:
- Alpine.js for reactive UI without build steps
- Tailwind CSS for styling
- Shiki for syntax highlighting
- WebSocket API for live updates
| Technology | Purpose |
|---|---|
| Cloudflare Workers | Serverless edge compute platform |
| Durable Objects | Stateful, coordinated compute with SQLite |
| Workers Assets | Static asset serving |
| WebSockets | Real-time bidirectional communication |
| Alpine.js | Lightweight reactive framework |
| Tailwind CSS | Utility-first CSS framework |
| Shiki | Beautiful syntax highlighting |
- Node.js 18+ installed
- pnpm package manager
- A Cloudflare account (free tier works!)
- Wrangler CLI installed globally or via pnpm
# Clone the repository
git clone https://github.com/yourusername/webhookflare.git
cd webhookflare
# Install dependencies
pnpm install
# Start local development server
pnpm run devVisit http://localhost:8787 to see your webhook tester in action!
The development server will:
- Run your Worker locally with hot-reload
- Persist Durable Object data between restarts
- Simulate the production environment
# Authenticate with Cloudflare (first time only)
wrangler login
# Deploy to Cloudflare Workers
pnpm run deployYour webhook tester will be live at https://durable-object-starter.<your-subdomain>.workers.dev
- Generate a webhook URL: Open the app to automatically generate a unique endpoint
- Send test requests: Use curl, Postman, or any HTTP client
- Watch in real-time: Requests appear instantly in the dashboard
Basic POST request:
curl -X POST https://your-app.workers.dev/hook/abc12345 \
-H "Content-Type: application/json" \
-d '{"event": "user.created", "userId": "12345"}'With query parameters:
curl -X POST "https://your-app.workers.dev/hook/abc12345?source=api&version=v2" \
-H "Content-Type: application/json" \
-d '{"data": "test"}'Testing Stripe webhooks:
curl -X POST https://your-app.workers.dev/hook/abc12345 \
-H "Content-Type: application/json" \
-H "Stripe-Signature: t=1234567890,v1=..." \
-d '{"type": "payment_intent.succeeded", "data": {...}}'GET request with headers:
curl -X GET https://your-app.workers.dev/hook/abc12345 \
-H "X-Custom-Header: CustomValue" \
-H "Authorization: Bearer token123"Each webhook URL is uniquely identified by its ID and can be:
- Shared with team members (via the
?id=parameter) - Used across multiple services simultaneously
- Bookmarked for repeated use
- Embedded in CI/CD pipelines
| Method | Endpoint | Description |
|---|---|---|
GET |
/ |
Homepage with webhook viewer UI |
GET |
/api/new |
Generate a new webhook ID |
GET |
/api/bin/{id} |
Fetch all requests for a webhook (JSON) |
GET |
/ws/{id} |
WebSocket connection for real-time updates |
ANY |
/hook/{id} |
Capture any HTTP method to this webhook |
The WebhookBin Durable Object exposes:
// Store a webhook request
captureRequest(
method: string,
headers: Record<string, string>,
body: string,
query: string,
metadata: string
): Promise<number>
// Retrieve all requests (latest 100)
getRequests(): Promise<WebhookRequest[]>
// Handle WebSocket upgrades (via fetch)
fetch(request: Request): Promise<Response>webhookflare/
βββ src/
β βββ index.ts # Worker entry point + Durable Object class
βββ public/
β βββ index.html # Frontend SPA with Alpine.js
βββ wrangler.jsonc # Cloudflare Workers configuration
βββ tsconfig.json # TypeScript configuration
βββ package.json # Dependencies and scripts
βββ README.md # This file
src/index.ts - Contains:
WebhookBinDurable Object class with SQLite storage- Worker fetch handler for routing
- RPC method implementations
- WebSocket connection management
public/index.html - Contains:
- Alpine.js reactive application
- WebSocket client implementation
- UI components and styling
- Shiki syntax highlighting integration
wrangler.jsonc - Configures:
- Durable Object bindings
- SQLite migrations
- Static assets directory
- Observability settings
This project demonstrates several Cloudflare Workers platform concepts:
- SQLite Storage: Persistent database within each Durable Object
- RPC Methods: Calling methods on Durable Objects from Workers
- WebSocket Handling: Managing persistent connections
- State Management: Coordinating multiple WebSocket sessions
- Naming: Using
idFromName()for deterministic object IDs
- Request Capture: Extracting and storing HTTP request details
- Cloudflare Metadata: Leveraging
request.cffor geolocation - Broadcasting: Sending messages to multiple WebSocket clients
- Migrations: Using SQLite migrations for schema management
- Static Assets: Serving frontend files with Workers Assets
- Durable Objects: Easy, Fast, Correct β Choose three
- Building a Distributed Chat Application
- SQLite in Durable Objects
- RPC Protocol for Durable Objects
The wrangler.jsonc file configures your Worker:
No environment variables required! Everything runs out of the box.
To use a custom domain:
# Add a route in wrangler.jsonc
"routes": [
{ "pattern": "webhooks.yourdomain.com/*", "custom_domain": true }
]
# Deploy
pnpm run deploy- Data Isolation: Each webhook ID creates a separate Durable Object instance
- No Authentication: URLs are generated randomly but are publicly accessible
- Data Retention: Only the 100 most recent requests are stored per webhook
- CORS: Enabled for all origins (modify in
src/index.tsif needed) - Rate Limiting: Not implemented (consider adding for production use)
For production deployments, consider adding:
- Authentication: Require API keys for webhook creation
- Rate Limiting: Implement per-webhook request limits
- TTL: Auto-expire webhooks after a certain time
- Webhook Secrets: Verify webhook signatures (e.g., Stripe, GitHub)
- Access Control: Password-protect webhook viewers
WebhookFlare is designed to be cost-effective on Cloudflare's platform:
| Resource | Free Tier | Pricing Beyond Free Tier |
|---|---|---|
| Worker Requests | 100,000/day | $0.15/million requests |
| Durable Objects | First 1M requests free | $0.15/million requests |
| SQLite Storage | First 1 GB free | $0.50/GB-month |
| WebSocket Connections | No charge | Included in DO requests |
Estimated costs for typical usage:
- 1,000 webhooks/day with 10 viewers: ~$0.50/month
- Development/testing: Stays within free tier
Learn more: Cloudflare Workers Pricing
- Local dev: Ensure you're using
ws://notwss:// - Production: Check that WebSocket upgrade headers are not blocked by proxies
- Browser: Look for errors in the browser console
- Check the webhook URL matches the ID in the browser (
?id=...) - Verify the request is reaching Cloudflare (check Workers logs)
- Ensure WebSocket is connected (green indicator in UI)
- Ensure migrations are properly configured in
wrangler.jsonc - Delete and redeploy if schema changed:
wrangler delete && pnpm run deploy
# Clear local Durable Object state
rm -rf .wrangler/state
# Restart dev server
pnpm run devContributions are welcome! This project serves as educational content for the Cloudflare developer community.
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Make your changes
- Test locally:
pnpm run dev - Deploy to your Workers account:
pnpm run deploy - Submit a pull request
- Cloudflare Community: community.cloudflare.com
- Discord: Cloudflare Developers Discord
- Docs: developers.cloudflare.com
- Issues: GitHub Issues
Ready to build your own? Check out the Cloudflare Workers documentation to get started!
{ "name": "durable-object-starter", "main": "src/index.ts", "compatibility_date": "2025-11-11", // Define Durable Object bindings "durable_objects": { "bindings": [ { "class_name": "WebhookBin", "name": "MY_DURABLE_OBJECT" } ] }, // SQLite migrations "migrations": [ { "tag": "v1", "new_sqlite_classes": ["WebhookBin"] } ], // Static assets configuration "assets": { "directory": "./public" }, // Enable observability "observability": { "enabled": true } }