Summary
The /jobs/search and /jobs/summary endpoints are safe, idempotent reads that carry a
JSON body (the filter/grouping is too rich to fit in a URL query string). Today they are served
over POST, which is a semantic mismatch: POST signals "this may change state — do not cache,
do not retry."
RFC 10008 — The HTTP QUERY Method (Proposed Standard,
June 2026) defines exactly the method we want: safe, idempotent, cacheable, and body-carrying.
OpenAPI 3.2 (Sept 2025) can
now describe it via the query operation / additionalOperations.
This issue proposes we track migrating these endpoints from POST to QUERY. The goal here is to record the rationale, and the exact prerequisites.
Endpoints in scope
POST /jobs/search → QUERY /jobs/search
POST /jobs/summary → QUERY /jobs/summary
- (any future
search/summary-style endpoints, incl. the gubbins extension mirrors)
Why it's worth doing
- Correct, self-documenting semantics. The method itself declares the operation is a safe,
idempotent read. This is not cosmetic — it's the contract every intermediary reads.
- Safe automatic retries. The generated client runs over
azure-core + httpx2. Retry
policies deliberately do not retry a POST on a dropped/half-open connection (it's not
idempotent). QUERY is idempotent, so transient network failures on search could be retried
transparently.
- Future cacheability. RFC 10008 responses are cacheable keyed on URI + request body.
Size of the win is modest
The headline RFC 10008 benefit — cacheable reads at shared intermediaries — is largely nullified
for DiracX: search results are per-user, bearer-token-gated, and highly volatile, so there is
little a shared cache can safely reuse. The concrete wins for us are the two narrower ones above
(semantics + safe retries). This is a hygiene / future-proofing change, not a performance fix.
Blockers (why this is tracking-only today)
- FastAPI emits OpenAPI 3.1. QUERY support is still an open FastAPI discussion
(fastapi/fastapi#15839) and not shipped.
autorest does not consume OpenAPI 3.2 additionalOperations. diracx-client is
autorest-generated from the server's OpenAPI spec (pixi run generate-client).
Proposed migration path (when unblocked)
Do it backward-compatibly, not as a hard cutover:
- Add
QUERY as an alias for the existing handler, keeping POST registered:
@router.api_route("/search", methods=["POST", "QUERY"], ...). No logic change — the handler
body is identical.
- Regenerate the client so
diracx-client prefers QUERY, and confirm the CLI/API layers
still call through unchanged.
- Deprecate
POST on these routes only after a release cycle, once servers and the pinned
client have shipped QUERY support and deployment intermediaries are verified.
Keeping both methods on the same handler means zero behavior change and no flag-day for older
clients or reverse proxies.
Acceptance criteria / definition of done
References
Summary
The
/jobs/searchand/jobs/summaryendpoints are safe, idempotent reads that carry aJSON body (the filter/grouping is too rich to fit in a URL query string). Today they are served
over
POST, which is a semantic mismatch:POSTsignals "this may change state — do not cache,do not retry."
RFC 10008 — The HTTP QUERY Method (Proposed Standard,
June 2026) defines exactly the method we want: safe, idempotent, cacheable, and body-carrying.
OpenAPI 3.2 (Sept 2025) can
now describe it via the
queryoperation /additionalOperations.This issue proposes we track migrating these endpoints from
POSTtoQUERY. The goal here is to record the rationale, and the exact prerequisites.Endpoints in scope
POST /jobs/search→QUERY /jobs/searchPOST /jobs/summary→QUERY /jobs/summarysearch/summary-style endpoints, incl. thegubbinsextension mirrors)Why it's worth doing
idempotent read. This is not cosmetic — it's the contract every intermediary reads.
azure-core+httpx2. Retrypolicies deliberately do not retry a
POSTon a dropped/half-open connection (it's notidempotent).
QUERYis idempotent, so transient network failures on search could be retriedtransparently.
Size of the win is modest
The headline RFC 10008 benefit — cacheable reads at shared intermediaries — is largely nullified
for DiracX: search results are per-user, bearer-token-gated, and highly volatile, so there is
little a shared cache can safely reuse. The concrete wins for us are the two narrower ones above
(semantics + safe retries). This is a hygiene / future-proofing change, not a performance fix.
Blockers (why this is tracking-only today)
(fastapi/fastapi#15839) and not shipped.
autorestdoes not consume OpenAPI 3.2additionalOperations.diracx-clientisautorest-generated from the server's OpenAPI spec (
pixi run generate-client).Proposed migration path (when unblocked)
Do it backward-compatibly, not as a hard cutover:
QUERYas an alias for the existing handler, keepingPOSTregistered:@router.api_route("/search", methods=["POST", "QUERY"], ...). No logic change — the handlerbody is identical.
diracx-clientprefersQUERY, and confirm the CLI/API layersstill call through unchanged.
POSTon these routes only after a release cycle, once servers and the pinnedclient have shipped
QUERYsupport and deployment intermediaries are verified.Keeping both methods on the same handler means zero behavior change and no flag-day for older
clients or reverse proxies.
Acceptance criteria / definition of done
QUERYoperationfor these routes.
autorestclient generation produces a workingQUERYcall;pixi run generate-clientis green and
diracx-clientsearch/summary calls pass their tests.POSTcontinues to work for one deprecation cycle (no client flag-day).POST.References
diracx-routers/src/diracx/routers/jobs/query.py(search,summary)