← All posts

Developers · 2 min

Read the SQL on the wire with Caddy and mitmproxy

Your backend builds SQL from the user's filters. Route the Postgres connection through mitmproxy at runtime with @viaalink/caddy-api-client, read the real query, and put the route back. Nothing restarts.

  • caddy
  • mitmproxy
  • postgres
  • sql
  • debugging
  • developer-tools

TL;DR: The SQL your app sends only exists at the moment it runs. @viaalink/caddy-api-client can send one Postgres connection through mitmproxy and back while Caddy keeps running. The decoder shows the query, its parameters, the rows and any errors. Watch the walkthrough on YouTube (2:38).


The query nobody wrote

A backend builds its SQL from what the user picked: filters, categories, a sort order. The code shows the pieces. It does not show the statement that reaches the database.

The database’s own query log would answer that, but it is often switched off, out of reach on a managed database, and it mixes every client’s queries together. You want the statements from one connection, as they were sent.

Put the proxy in the path

Caddy can route raw TCP with its layer 4 app (caddy-l4). The client swaps that one route through mitmproxy and back at runtime, with no restart. Caddy keeps running, and only the Postgres route changes.

mitmproxy captures the bytes. The Postgres decoder that ships with the library turns them into the SQL, its parameters, the rows and the errors. It also hides passwords, so the log you keep does not contain them.

What the demo finds on the wire

The walkthrough runs a product search over a real Playwright recording. Two bugs show up only when you read the traffic:

  • A category that matches nothing. The app sends electronics, while the data says Electronics. The query returns zero rows and no error, so the page just looks empty.
  • A sort on a column that does not exist. Choosing “Sort by Rating” sends a query on rating. The app only says “something went wrong”. Postgres says exactly what is wrong: column "rating" does not exist.

Neither bug is in the code you would read first. Both are obvious in the statement.

How it compares to ngrok

ngrok has added TCP connection inspection that shows connection metadata. Our angle is different: you read the SQL inside the connection, and it happens locally, on your machine, against your own Caddy.

Get started

The narration uses an AI voice clone of Kelvin’s voice. The screen recording is real: a Playwright run against the product search.

Contact

Have a topic for us?

Write to us.