Skip to content
Backend Engineering GuidesHTTP QUERY
Modules 0/5

Backend Engineering Guides · HTTP QUERY

GET's promise,
POST's payload.

The HTTP QUERY method, explained so anyone can follow it.

RFC 10008 adds QUERY: an HTTP method that reads without changing anything, like GET, yet carries its question in a request body, like POST.

Written byShree Kumar Sharma
5short modules
6 minreading time
2live demos
RFC 10008Standards Track
ONE SEARCH, SAFE TO REPEAT CACHE CLIENT YOUR API QUERY 200 OK SAFE IDEMPOTENT BODY
SCROLL

01Why HTTP needed it

A read with a body

A search is a read. It should be safe to repeat and safe to cache. Until now, the only way to send a large search was a method that promises neither.

MotivationSafeIdempotentCachingMust know
IN THE URL /contacts?select=surname,givenname,email&limit=10&match=email%3D*%40example.* IN THE BODY QUERY /contacts select=surname,givenname, email&limit=10&match= "email=*@example.*"

Think of a library enquiry slip

A short question you can call across the counter. A long one you write down, but the only form on the desk is the returns form, and it makes the librarian think you are changing their records. QUERY is a proper enquiry slip: written down, and clearly just a question.

Try it: one search, three methods

The same contact search, sent three ways. Switch the method and watch what the request can promise.

Arrow keys switch too
request.httpQUERY

        
  • Carries a request bodyRoom for a long, structured search
  • Keeps the search out of the URLNot logged, bookmarked or size capped
  • SafePromises not to change server state
  • IdempotentCan be retried after a network failure
  • Response can be cachedBy browsers, proxies and CDNs
Verdict

Where GET and POST fall short

Both work for searches today. Each one pays for it somewhere.

GET

The search has to fit in the URL

URLs have practical length limits, end up in server logs and get saved in bookmarks and history. A complex filter does not fit cleanly in a query string, and you may not want it written down everywhere.

POST

A body, but no promises

A body holds any search you like, but POST is neither safe nor idempotent by definition. Clients and proxies cannot assume a retry is harmless or that the response is fine to cache.

02Method comparison

The best of both

QUERY takes the guarantees of GET and the body of POST. The highlighted column is the new one.

SemanticsRFC 9110RFC 10008
GETQUERYPOST

Think of the post office counter

A postcard carries a few words on the outside for every postman to read. A parcel carries anything, but it is marked "may change things", so nobody dares send it twice. QUERY is a sealed envelope stamped "question only": it holds a lot, and anyone can safely resend it.

GET, QUERY and POST side by side

PropertyGETQUERYPOST
Safe, no side effectsYesYesNo
Idempotent, safe to retryYesYesNo
Has a request bodyNoYesYes
A URI for the query itselfBy definitionOptional, via LocationNo
CacheableYesYesOnly for a later GET or HEAD

Per RFC 10008 and RFC 9110.

03A QUERY on the wire

Same shape, new rules

It reads like a POST. The method name is what changes the rules for everyone in between.

ExampleRequest bodyRetries
CLIENT SERVER QUERY 200 SAME ANSWER, AGAIN

Think of asking a shopkeeper a price

Ask the price of a kilo of rice twice and you get the same answer both times, and nothing in the shop changes. That is what idempotent means, and it is why a client may simply ask again after a dropped connection.

One request, one answer

requestHTTP/1.1
QUERY /contacts HTTP/1.1Host: example.orgContent-Type: application/x-www-form-urlencodedAccept: application/json select=surname,givenname,email&limit=10&match="email=*@example.*"
What to notice: the search rides in the body, and the method says it is only a read.
response200 OK
[  { "surname": "Smith", "givenname": "John",    "email": "smith@example.org" },  { "surname": "Jones", "givenname": "Sally",    "email": "sally.jones@example.com" }]
What to notice: an ordinary JSON answer, which a cache may keep.
Because QUERY is idempotent, a client may resend it after a failure and expect the same answer.

04Status codes

What the server says back

The responses you are most likely to meet when a server supports QUERY.

Responses2xx3xx4xx
200RESULTS IN THE BODY303GET IT AT LOCATION415FORMAT NOT SUPPORTED422CANNOT RUN IT

Think of replies at a shop counter

"Here it is" is a 200. "Collect it from counter 3" is a 303. "We do not take that form" is a 415, and "we understand, but we cannot do that" is a 422.

What the server can answer

CodeMeaningGroup
200The query ran, and the results are in the bodySuccess
303Fetch the results with a GET at the Location URIRedirect
304Not modified, a conditional request matchedRedirect
400Content-Type is missing or does not match the bodyClient error
415The query format is not supported hereClient error
422Well formed, but the query cannot runClient error

Green works, amber redirects, red fails.

Send a Content-Type every time

Without one, the server cannot tell how to read the query and answers 400. Match it to the body you actually send.

05Questions and checklist

Asked in code review

What comes up when a team starts using QUERY, then a checklist before you ship it.

FAQCORSCachesChecklist
CAN WE USE QUERY? CHECK EVERY HOP. KEEP A POST FALLBACK.

Questions people ask

Can I use QUERY today?

Only where every hop supports it. Check your server framework, proxies and CDN first, and keep a POST fallback for clients that cannot send it.

How is QUERY different from POST?

Same body, different promise. QUERY says the request is safe and idempotent, so clients can retry it and caches can keep the answer. POST promises neither.

When should I still use GET?

For short lookups that fit in a URL, and for anything people may want to bookmark or share.

Can a QUERY response be cached?

Yes, and it can be revalidated with ETag and If-Modified-Since like a GET. The cache has to key on the request body as well as the URL, and many caches do not do that yet.

How does a client know which query formats work?

The server lists them, such as SQL, JSONPath or XSLT, in the Accept-Query header. A client can read it from an OPTIONS or HEAD response before it asks.

What if the server cannot run my query?

It answers 415 for a format it does not support, 422 for a well-formed query it cannot run, and 400 when the Content-Type is missing or wrong.

Does QUERY work with CORS?

Yes, but it is not a safelisted method, so a browser sends a preflight OPTIONS first. The server must list QUERY in Access-Control-Allow-Methods.

Can a query get its own URL?

Yes. The server can return a Location that repeats the same query with a plain GET, or a Content-Location that points at the result itself.

Before you ship QUERY

Clears the ticks above and every module you marked done.