The HTTP QUERY Method specification
httpwg.org
httpwg.org
Nothing in the existing spec prevents GET from having a body, though there isn't currently a semantic meaning to it.
This would fit perfectly, be more compatible and result in a simpler spec and protocol.
But you wouldn't for QUERY?
This is backwards compatible and in many cases will just work since GET with body is already syntactically valid.
No, any good HTTP client or server allows unknown/new HTTP methods. If they are not recognized, they are treated as POST. This is a requirement of the HTTP spec.
I've already used QUERY in a few places, and it basically universally just works. Adding a request body to GET would practically be much harder to deploy and depend on.
A few years ago PATCH was added, and back then there was a bit more friction with some HTTP implementations only allowing a fixed set of methods, but this is mostly not true anymore.
That's not true.
"An origin server SHOULD return the status code ... 501 (Not Implemented) if the method is unrecognized or not implemented by the origin server." https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5....
If the params for the search are so many or so big that they don't fit in a single url, how could you use that as a cache key?
Right now you can:
* Pass the arguments as parameters
* Pass them on the request body. I personally done it on apis for games in unity/ios/android for almost a decade). Other products like Elasticsearch count on that as part of the core product.
* Semantically create searches in the server with POST /search
In the previous two examples, you can return a redirect to the search results (like /searches/33) with perfect caching/indexing, and delegate to the server the cache algorithms.
With things like Vary, Etags, Conditional fetchs, Content-Encoding, Content-Type, Cache-Control, Expires that the spec barely grasp, adding a huge body is something that a cache server/cdn will not implement.
So again. What is this spec solving?
The way many caching systems work, by hashing the body and using the hash as the cache key.
b) Unless your cache keys are publicly listable, this is not a security issue. And from a privacy perspective, GET requests are usually cached by path+params, and since search queries are usually in params these days, again, nothing changes.
That's not to say you shouldn't use cryptographic hash functions for keys, just that nothing really changes with this new verb.
Even if the token cache keys were properly namespaced, any cache key with a "token:" prefix would be readable, even if was used for other purposes than to store a token object. All that would be needed is the key suffix. The remediation of the vulnerability I found included proper cache key namespacing, as well as hashing with an HMAC (since tokens were being stored in plaintext).
So just sharing a real-world scenario where a lack of namespacing (and other caching mistakes) produced a vulnerability.
New and clearly distinct type of requests, new practices.
It isn't just the size of the request that makes people not want to put them in the query string, it's use of the query string over decades.
I think the horse has bolted here. With HTTP/2 (and often without), URLs can be _very_ long.
Hash
So much so that I added support for it to my own server and client libraries. Which means that adding support for QUERY will be trivial (yay!)
As an aside, I also support DELETE with a body.
[1]: https://bugs.chromium.org/p/chromium/issues/detail?id=14801 [2]: https://bugs.chromium.org/p/chromium/issues/detail?id=452335...
So while I agree that it would be nice if everyone respected a "living standard", my hopes for middleboxes to comply are not high.
But - are you asking the server to create/update a resource (POST and PATCH semantics), or are you asking the server to provide you a _view_ of a resource? I'm thinking of query languages like GraphQL and jq here, where a client is making a potentially complex request for a view of the data returned in a structure which is most efficient for them.
The client isn't intending to _modify_ that data in any way, but it is asking for a potentially computationally expensive view of that data.
The way we've done it so far is to either: 1. Cram that complex filtering and view-building into the GET request URI (brittle, not guaranteed URI will function correctly past ~2,000 characters, not clearly defined maximum limit to request URI length in specs) 2. Send the query in a POST request - which works, and is almost what we want, but semantically feels a bit icky, because a query is more cacheable than your average POST, see below:
> caching etc may not make sense.
Perhaps you have a large and complex but static dataset _which will never change_ which nevertheless has many ways of filtering it -- all of which would benefit greatly from e.g. CDN caching.
I'm in two minds here. I half agree with you that POST works fine as the wording of the RFC spec is general enough that what we want to do with QUERY works with it fine as-is. But then I also see how _de-facto_ REST API design conventions have worked against this and treat POST as "create a new resource everytime", and trying to stuff endpoints that go "filter and restructure the responses to my specification" alongside feels.... wrong.
(There is of course the argument that you shouldn't be mixing and matching 'strictly' 'RESTful' patterns with other patterns like the GraphQL one which does away with some of the REST HTTP method semantics in favour of defining the operation type in the request data itself, and of course both of these things are patterns which happen to use HTTP and don't need to influence HTTP spec itself...... But still.... I really want that QUERY lol)
There’s also an option to use the following semantics (with a number of nice bonuses):
POST /resource/views
{some definition of response structure and allowed parameters}
GET /resource?view=…¶ms=…
The whole idea of GraphQL revolves around the possibility of client to query the whole graph of the persistent model. This idea itself is questionable for many reasons - coupling, security etc. In many cases where I have seen it, classic REST with HAL (client-server) or plain old SQL (server-server) would be more than enough.This is no different than how any other caching proxy is expected to operate given a set of inputs. It's never been up to the proxy to interpret if the queries "name=joe%20user" and "first=joe&last=user" are the same, it just passes the input along to its upstream and then locally stores the result, assuming that the same input will occur again and save a trip to upstream.
namely, urls are finite, bodies are infinite
specifically, a URL can basically always be fully read and cached in memory in a server, a request body cannot
I don't mind caching, but please make it deterministic.
It may sound cool and efficient on paper, just trim the whitespace and sort all json dictionaries right? But in practice it adds too much complexity, eventually implementations of this semantics will start to drift between cache and real backend. Case in point: SAML XML signatures.
This is how one creates a cache poisoning vulnerability. If a request is normalized as a cache key, use the normalized request when sending to the backend also. If you don’t trust that process you shouldn’t trust it as the cache key either.
Proxies should be dumb, just hash the raw string for the cache key.
Thinking, will it be possible to send HTTP/1.1 QUERY requests? HTTP/2 QUERY requests?
Or will it be for HTTP/3 or something even higher?
Well the examples given in the document seems to indicate that it will be possible to use with HTTP/1.1 even
QUERY /contacts HTTP/1.1
Host: example.org
Content-Type: example/query
Accept: text/csv
select surname, givenname, email limit 10If you read my comment as 'QUERY will automatically become POST', that's not what I meant.
By "support", do you mean "will not crash on encountering an unexpected verb"?
WEBDAV and CALDAV rely on a slew of new HTTP verbs; but IME only specialized servers actually support these verbs, in the sense of knowing what to do with them. There's an addon module for Apache that supports WEBDAV, but the logic to support CALDAV is insanely complicated, and nearly all implementations rely on a real database server.
You are right, code is needed to 'do something' with the method, but webservers, clients, application frameworks and proxies typically don't need to change for someone to start handling (for example) PROPFIND requests.
The same is true for QUERY. In most (but not all) cases you can already start using it today.
I tried to build a CALDAV stack in PHP. But I didn't want to use a database; I wanted all resources to be files. I made something that worked, and passed most tests; but the RFCs were breeding faster than I could keep up. I wasn't trying to make a product, I just wanted a "simple" calendar server for my own use, and I didn't have the time or inclination. I gave up, and switched to Radicale.
Last week I deployed Baikal; it seems very nice. Well done!
Or will the QUERY method end up with the same fate as GraphQL - wherein it's more effective and "secure" in a server-to-server setup and the client only deals with REST.
I don’t think anyone suggests we all open our databases to the web; but if you choose to do so, or if you happen to work on a modern database, like Elasticsearch or CouchDB, which accept queries via HTTP, now there’s a better way to implement queries in regard to caching.
That being said: I’ve been wondering for a long time what a backend API could look like that used SQL instead of JSON as the query format - not to pass it to the database verbatim, but with an application layer that speaks SQL, applies business logic, queries the database, and responds in SQL. That would save a lot of reinvented wheels in terms of filtering, ordering, joining, and so on, and give developers a query language they already know. And suddenly, having a QUERY method available sounds useful, too :)
And just like with POST, whether or not this is actually the case in a given API, depends entirely on the server-side implementation.
Look, I get it. We want to make rules. Rules are good. Rules define things.
But in a world where so many "REST APIs" are actually "RESTful" or REST-ish, or "actually about as REST as a Pelican, but we really liked the sound of the acronym", I wonder if adding one more rule to the pile is really going to substantially change anything.
A majority of APIs don't even use all of the existing HTTP verbs, or HTTP response codes for that matter. And every API is free to make up their own rules. I had the dubious pleasure of consuming APIs that required GET with both a body and urlparams, and which returned 200 on error, but with an `{error: {...}}` object in the body. The crown jewel so far, was an authentication system that had me send credentials as multipart/form-data, with a PUT request (because you inPUT the credentials, see? Not a joke, that was the rationale given to me by the dev who made it.)
That you cannot rely on consistency is not a reason to have none at all.
Destructive actions can and are also hidden behind a GET. When such an endpoint gets called indiscriminately by the rest of the internet it usually becomes the faulty implementation's problem.
Presumably the same would be true for QUERY.
Those are 2 very big if's.
> That you cannot rely on consistency is not a reason to have none at all.
I want consistency. I stated as much in my post: "Rules are good." I'm just not convinced adding more rules to a collection of rules that already isn't consistently applied in the wild, will give me more consistency.
Semantically, it's an error that the frontend can handle and shouldn't show up as "unexpected error" in network logs. Many people treat HTTP error codes as exceptions / real infrastructure problems.
I mean, we build rich clients. You can't simply auth with a user/password header when you get a 401/403 and your browser shows an alert. It's not 2003 anymore.
No it isn't. Semantically `200 OK` means a request was successful. That isn't me saying that, that's the RFC:
https://datatracker.ietf.org/doc/html/rfc1945#section-9.2
The semantically correct message to signal to the frontend that the error originated on its side, and thus has to be fixed by it, is a 4xx response, period.
https://datatracker.ietf.org/doc/html/rfc1945#section-9.4
> Many people treat HTTP error codes as exceptions / real infrastructure problems.
Doesn't change the fact that they aren't exceptional. A != 2xx response code is not an exceptional occurrence. As soon as I talk to a networked process, especially one that I don't control, I am talking to unreliability, and being able to deal with that is normal program operation, not an exception.
> I mean, we build rich clients. You can't simply auth with a user/password header when you get a 401/403 and your browser shows an alert. It's not 2003 anymore.
So?
What exactly does the auth method I'm using have to do with the server using semantically correct return codes?
Luckily, I also consume a lot of good APIs in my code. Guess what they send back when my client messes up something during OAuth. Hint: It's not `HTTP/200 OK`
That is because there are essentially only 3 verbs in HTTP: GET, POST, and OPTIONS (needed for CORS). All the others are (from a protocol POV) rebranded POST or very specialized (like TRACE or HEAD).
This new method in particular is meant to be useful for proxies, so that they can cache more without risking breaking old stuff
In mine, I was just caching raw sql queries, so it was literally just text/sql encoding, the query in the body, some metadata in headers, and a sqlalchemy engine to execute the query.
It's basically a way to get around the non-idempotency of POST and URI limitations of GET.
So, the request content/body can have "non-sql-like" queries? can it be GraphQL? or even plain English? - of course, assuming that the server knows how to resolve the query.
QUERY /graphql
Content-Type: text/graphql
{too lazy to write valid gql}
so is this QUERY /elasticsearch
Content-Type: application/json
{"name": "alice"}
Even this QUERY /my-ai-image-gen
Content-Type: text/plain
Draw me a picture of a cat
It's entirely up to the software to decide how to handle the request.Here's a great post on it- https://httptoolkit.com/blog/http-search-method/ and earlier HN thread- https://news.ycombinator.com/item?id=36095032
So if you are using POST to query, you can't cache the response. You have to resort to POST/GET. With QUERY, you have idempotent requests with cacheable responses you can directly return.
> You can just handle your POST request idempotently
You actually can't though, and you don't usually have to look far to run into a case where this gives the wrong behavior. Better to just have a separate verb.
You can't pre-fetch a POST request, or re-try after a timeout. Because you have to consider that the POST request could have unintended consequences if sent too many times.
You just document that that POST endpoint doesn't actually modify data.
A great example of this are OpenAI completions.
https://stackoverflow.com/questions/978061/http-get-with-req...
When designing an API, and if spec compliance is not key, I wonder if client-compliance would become the issue (clients refusing to emit a GET body).
But adding a new new method is probably overall better and matches people's understanding of the implementation and interpretation of GET, even if (with extensions) GET can have a body people don't think of it like that. So a new method with defined semantics and interpretation avoids a whole bunch of sideshow debate about if GET with a body is possible or appropriate.
sending a body with a GET request does not suggest the recipient server will receive that body, in general
Most middlebox will equally drop any HTTP verb which is not whitelisted.
Even the extension of WebDAV, which are 15 years old are still commonly blocked.
my point is that a middlebox that receives a GET request with a body may drop the body from the request and still forward the request onwards, without the body
these are categorically different outcomes
That's wrong. A lot of people just don't understand the difference between SHOULD and MUST when reading standards. The standard just says that you shouldn't rely on servers accepting it unless they tell you.