Grove · · 5 min read

Grove 0.12 & 0.13 — Replay Any Request, Bundle Your Environment, Catch Every Webhook" published

Grove now remembers what happens to your apps: replay failing requests, export your whole environment as a single file, catch and re-deliver webhooks, and turn any captured request into a Pest test.

Grove 0.12 & 0.13 — Replay Any Request, Bundle Your Environment, Catch Every Webhook" published

0.12 — Replay, reproduce, and a splash of color in your SQL

Why: a failing request shouldn't be a one-time event

You know the loop. Something posts to your app, it 500s, and by the time you've added a dd() the moment is gone — you're re-triggering the flow by hand, re-filling the form, re-clicking the button. Because Grove sees every request, it can just keep them. So now it does.

How: expand it, replay it

The Requests timeline (and grove requests) now shows a stable id for every request, and you can expand any one to see its full headers and body. Then you replay it:

grove requests
# #142   14:03:21.882   500   POST     18ms   myapp   /checkout

grove replay 142

replayed → 500 in 16ms (see it in grove requests)

Replay re-sends the captured request through Grove's whole pipeline, so it hits your app exactly as the original did — same method, headers, body — while you edit the code. Fix, replay, fix, replay. No clicking through the UI again. It's a zero-config, framework-agnostic Telescope that works for Laravel, WordPress, a raw PHP file, or a Node container all the same.

Why: "works on my machine" should be a file you can hand someone

A grove.toml already captured your PHP/Node versions and services. But a teammate cloning your repo still needed your data to see what you see. So we made the whole environment portable.

How: bundle it, ship it

grove bundle export

wrote myapp.grovebundle — share it; restore with grove bundle import <file>

That single file contains the grove.toml, the .env, and the database — a SQLite file copied in, or a MySQL/Postgres dump taken through Grove's bundled tools. On the other end:

grove bundle import myapp.grovebundle

unpacks → brings the environment up → loads the database

restored to ./myapp — live at https://myapp.test

Reproducible dev environments, no Docker required. It's the fastest onboarding story we know: hand someone a file, they run one command, they're looking at the same running app with the same data.

And a small joy: SQL that isn't a wall of gray

The built-in Database panel's query editor now highlights your SQL — keywords, strings, numbers, comments — with a tiny, dependency-free highlighter. Small thing. Makes writing a quick select feel a lot nicer.

0.13 — Grove catches webhooks (and writes your tests)

Why: everyone can catch mail; nobody catches webhooks

Herd popularized the local mail-catcher, and it's genuinely great. But the modern pain isn't just email — it's webhooks. Stripe, GitHub, Slack, all POSTing to endpoints you can't easily see or re-run locally. You end up on webhook.site, copying payloads by hand. Grove already owns your DNS, TLS, and tunnels — so it can be the catcher.

How: point, capture, re-deliver

Any request to /__grove/hooks/<bucket> on a site is captured and answered 200 — it never touches your app. Expose it publicly with the tunnel you already have:

grove share myapp

https://calm-otter-1234.grove.li → myapp.test

Point Stripe at https://calm-otter-1234.grove.li/__grove/hooks/stripe, trigger an event, and it lands in the Webhooks panel (and grove hooks) with its full payload. Now the good part — re-deliver it to your real handler while you work:

grove hooks

#7 15:22:01 POST stripe /__grove/hooks/stripe

grove hooks replay 7 --to https://myapp.test/stripe/webhook

delivered → 200 in 24ms

Trigger the real event once in Stripe. Replay it into your handler a hundred times. No more "let me make another test payment."

Why: the best test is the request that just broke

You captured the exact request that failed. That is your test case — you shouldn't have to retype it.

How: copy it as a test

From any request or webhook, copy it as a curl command, a .http file, or a Pest feature test:

grove request 142 --as pest
<?php

it('POST /checkout responds', function () { $response = $this->postJson('/checkout', [ 'sku' => 'A1', 'qty' => 2, ]);

$response-&gt;assertStatus(500);

});

Grove reads the captured JSON body and renders it as a PHP array, sets the method and path, and asserts the status. Turn a bug you just saw into a regression test in one click — or grab the same request as curl to share in an issue. (The buttons are right there in the app, too.)

And yes — it's lime now

The app icon traded its orange for a bright lime. Small, cosmetic, and honestly kind of cheerful sitting in the menu bar. 🍋‍🟩

The throughline

Every one of these features exists because Grove sits at a layer bolted-on tools don't reach. It's the proxy, so it can remember and replay requests. It owns your config, secrets, and databases, so it can bundle a whole environment. It runs the tunnel, so it can be your webhook catcher. It captured the request, so it can write the test.

None of this gates the free core — serving sites, HTTPS, PHP/Node, databases, mail, tunnels, the request timeline, webhooks, replay, bundles, test-gen: all free and open source, forever.

# already installed? you're on your way here automatically
grove requests        # what just happened
grove replay <id>     # make it happen again
grove bundle export   # hand it to a teammate
grove hooks           # what the internet just sent you

Update the app (it does that itself), and go poke around. Expand a request and hit Replay. Point a webhook at Grove. Copy something as a Pest test. It's the same local dev environment you had yesterday — it just remembers a lot more now. 🌱🌳

Grove is free and open source. Try it at elyracode.com/grove.