Building a Webhook Debugger: 8 Obstacles I Didn't Expect
![]()
The "Simple" Weekend Project
I started with a simple goal: build a webhook debugging tool. Something like webhook.site or the ngrok inspector, but open-source and free.
I thought it would take 4 hours. It took 2 days.
Here is the technical post-mortem of why a "simple" app became a masterclass in serverless architecture, state management, and the dangers of bleeding-edge frameworks.
1. The Serverless Memory Trap
Direct Answer: Never use in-memory storage (like a JavaScript Map) in a serverless environment. Serverless functions are ephemeral and stateless; data saved in one execution context is lost in the next. Always use an external store like Redis or a database.
I started by storing webhooks in a global Map<string, Webhook[]>.
It worked perfectly on localhost. I could generate a URL, post data to it, and see it appear.
Then I deployed to Vercel.
The result: I would post a webhook, get a 200 OK, and then refresh the page to see... nothing. The viewing client hit Server Instance A (which was empty), while the webhook hit Server Instance B (which stored the data in RAM and then died).
The Fix: I migrated to Upstash Redis. It provides a serverless-compatible Redis instance that acts as the persistent "brain" between the ephemeral Vercel functions.
2. The Next.js 16 "Canary" Mistake
Direct Answer: Avoid upgrading core frameworks (like Next.js) to beta or release candidate versions in the middle of a short-timeline project. Breaking changes in routing, caching, and API signatures can cost hours of debugging time that outweigh the benefits of new features.
I made the classic developer mistake: "Oh, Next.js 16 is out? Let me upgrade.
The consequences:
- Route Handlers: The syntax for accessing
paramschanged. What was synchronous in Next 14 became a Promise in Next 16. My API routes crashed immediately. - IP Address: Accessing
request.ipwas removed or changed, forcing me to parsex-forwarded-forheaders manually. - Async Components: JSX transform issues appeared with certain async server components.
Time lost: 3+ hours.
Lesson: For a weekend hack, use the stack you know. Boring is fast.
3. The Windows Compatibility Blindspot
Direct Answer: Windows PowerShell parses quotes differently than Bash/Zsh. Single quotes
'are treated as literals in Bash but can cause issues in PowerShell command arguments. Always provide copy-paste examples for both shells.
Standard API documentation often assumes everyone is using Bash. The "Copy Curl" button generated this:
curl -X POST -d '{"hello":"world"}' ...
But in PowerShell, single quotes around the JSON body can cause parsing errors or inhibit variable expansion in ways users don't expect. Since I want this tool to be universally useful, ignoring Windows semantics wasn't an option.
The Fix: I added a specialized "Copy for PowerShell" button that escapes the JSON correctly for Windows users.
4. The "Monorepo" Mess
I initially put this project inside a monorepo with my other experiments.
The problem: Vercel's build system got confused about the root directory. GitHub Actions failed because they couldn't find the package.json. Git history became a polluted mix of three different projects.
The Fix: I nuked the folder and started a fresh, standalone repository.
Lesson: If it's a distinct product, give it a distinct repo.
5. Field Naming: The Case of the Missing Timestamp
My Redis database stored data as createdAt (camelCase).
My frontend interface expected created_at (snake_case).
TypeScript said everything was fine because I had defined the type as created_at. But at runtime, the data from Redis didn't match the type definition.
The Result: The UI showed "Unknown Time" for every webhook.
The Fix: Aligned the types with the actual data structure. Types are only as good as your data validation (Zod is your friend here).
6. UX: Backend Working ≠ Product Working
I got the backend working. Creating a URL and capturing data worked. I thought I was done.
Then I actually used it.
- Issue: I had to manually refresh the page to see if a webhook arrived.
- Fix: Added auto-polling every 2 seconds.
- Issue: If I refreshed the page, I lost my unique URL and had to start over.
- Fix: Added URL persistence so
/e/my-webhook-idloads the correct session. - Issue: Large JSON bodies broke the layout.
- Fix: Added strict CSS overflow handling and a "Copy JSON" button.
The Pivot: Why Open Source?
I briefly considered gating this behind a login and charging for "Pro" features like longer retention.
But then I realized: That defeats the purpose.
The entire reason I built this was to have a tool that is faster than the existing paid options. Adding layers of authentication, payments, and tiered limits would just make it another bloated SaaS.
I wanted a tool that creates value in seconds, not one that requires a credit card.
So I pivoted to Open Source.
- License: MIT
- Cost: Free forever
- Goal: A portfolio piece that actually helps people.
Try It Out
If you are building an API integration or testing a webhook, give it a spin. No signup required.
Live Tool: webhookpro.ai-foil.com
Source Code: github.com/tiagofoil/webhookpro
Let me know if you break it. (You probably will).
