Chapter 10 of 14

Seeing the Request

Grove is the web server, so it sees every request before your application does and after it finishes. This chapter is about what that makes possible, and it is the feature you will miss most if you ever go back.

The problem

Something went wrong in a request you were not watching. A webhook arrived at three in the morning. A form submission failed for one user. An AJAX call returned 500 and the page swallowed it. In each case the request is gone: you have a log line if you are lucky, and the rest is reconstruction from memory and guesswork.

Worse, reproducing it means recreating the exact request — the same headers, the same body, the same session — which for anything involving a third party is an afternoon of work before you have even started debugging.

The hard way

The usual tools each see one slice. Your framework's debug bar sees requests that rendered a page — not the API call, not the webhook, not the one that died before the view. The access log sees a line with a status code and no body. Xdebug sees everything but only while you sit there with a breakpoint, which is no use for something that happened at three in the morning.

None of them can replay. And none of them can see the whole request, because none of them is the web server.

The timeline

grove requests
grove requests --site freddy

Every request Grove served: method, path, status, duration. Not a sample, not only the ones that rendered, not only the ones your framework knew about — every one, because Grove handled it.

Open one and you get its detail: headers, body, response, timing. The request that failed at three in the morning is there, with the payload that caused it.

Replay, and copy as

This is the part that changes how you debug. A captured request can be replayed — sent again, exactly as it arrived — so the loop becomes: replay, read the error, change the code, replay. Seconds per iteration, with no third party involved and no form to fill in again.

And it can be copied out:

  • as curl — to paste into a terminal, or to a colleague who does not have your machine.
  • as an HTTP file — for the client in your editor.
  • as a Pest test — and this is the one to notice. A bug you just reproduced becomes a failing test in one click, which is the difference between fixing a bug and fixing it permanently. The barrier to writing that test was never the typing; it was reconstructing the request.

The queries a request caused

With SQL capture on, the timeline shows the database queries belonging to each request — not a global query log you must correlate by timestamp, but the queries that request caused, in order, with their durations.

Which makes the N+1 problem visible rather than deducible. A page that runs 340 queries where you expected six is obvious in a list; it is nearly invisible in a page that renders in 900ms on a laptop with a warm local database, right up until it is a production incident on a table with a million rows.

grove explain

walks the causal chain for a request in one place: what arrived, what ran, what it asked the database, what came back. The value is that it is one place. Every part of that story exists somewhere in a conventional setup, in four different tools with four different clocks.

Leave it on. The cost is small and the benefit is retrospective: the request you wish you had captured is always one that has already happened. A tool you must remember to start is a tool that is off when you need it.

What you learned

  • Grove sees every request — including the ones that died before your framework was involved.
  • Replay turns debugging into a loop measured in seconds, with no third party and no form to refill.
  • Copy as Pest makes the fix permanent. The barrier to that test was reconstructing the request, not typing it.
  • Queries are attributed to the request that caused them, which makes N+1 visible instead of deducible.
  • Leave capture on. The request you wish you had is always one that already happened.
Next: in Chapter 11 the outside world: a public URL for your local site, and webhooks you can re-deliver instead of re-triggering.