Back to Blog
Tiago Duarte

Building a Webhook Debugger: 8 Obstacles I Didn't Expect

WebhookPro Debugger

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 params changed. What was synchronous in Next 14 became a Promise in Next 16. My API routes crashed immediately.
  • IP Address: Accessing request.ip was removed or changed, forcing me to parse x-forwarded-for headers 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-id loads 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).