POST /query is the main PgPaw endpoint.
curl -i -X POST http://127.0.0.1:8080/query \
-H "content-type: application/json" \
-d '{"sql":"select id, email from users where id = 7"}'
Request
{
"sql": "select id, email from users where id = 7"
}
The SQL must be one read-only PostgreSQL SELECT over
replicated tables. See
SQL classifier.
Public response
When every touched table is public, PgPaw materializes the query and returns a snapshot cursor:
HTTP/1.1 303 See Other
Location: /q/{hash}/{version}
Cache-Control: no-store
The response body is empty. Follow Location with
GET to read the JSON body.
Private response
When any touched table is private, PgPaw requires a bearer token.
curl -i -X POST http://127.0.0.1:8080/query \
-H "content-type: application/json" \
-H "authorization: Bearer $TOKEN" \
-d '{"sql":"select id, title from documents order by id"}'
Response:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: private, no-store
Private rows are returned inline because the result depends on the
caller's role and claims. Private rows are not stored in PgPaw's
shared query cache and do not receive a
/q/{hash}/{version} cursor URL.
See Cache-Control for the complete public/private header matrix.
Handler gates
If an Authorization header is present, PgPaw parses
it before the query is classified. Public queries do not require a
token, but a malformed token still causes 401.
Logs
Accepted queries emit logs like:
level=INFO target=pgpaw::http::query event=query_classified fingerprint=9a4f tables=users live=false scope=public
level=INFO target=pgpaw::http::query event=query_snapshot scope=public fingerprint=9a4f tables=users version=42 cursor=/q/9a4f/42 response=redirect snapshot_bytes=128
PgPaw logs the SQL fingerprint and tables, not raw SQL text.
